web-dev-qa-db-ja.com

std :: endlは、演算子<<をオーバーロードすると、不明なタイプになります

演算子をオーバーロードしました<<

template <Typename T>
UIStream& operator<<(const T);

UIStream my_stream;
my_stream << 10 << " heads";

動作しますが:

my_stream << endl;

コンパイルエラーが発生します:

エラーC2678:バイナリ '<<':タイプ 'UIStream'の左側のオペランドを取る演算子が見つかりません(または許容できる変換がありません)

my_stream << endlを機能させるための回避策は何ですか?

56
Kazoom

std::endlは関数であり、std::coutoperator<<を実装することによってそれを利用し、std::endlと同じシグネチャを持つ関数ポインターを取得します。

そこで、関数を呼び出し、戻り値を転送します。

次にコード例を示します。

#include <iostream>

struct MyStream
{
    template <typename T>
    MyStream& operator<<(const T& x)
    {
        std::cout << x;

        return *this;
    }


    // function that takes a custom stream, and returns it
    typedef MyStream& (*MyStreamManipulator)(MyStream&);

    // take in a function with the custom signature
    MyStream& operator<<(MyStreamManipulator manip)
    {
        // call the function, and return it's value
        return manip(*this);
    }

    // define the custom endl for this stream.
    // note how it matches the `MyStreamManipulator`
    // function signature
    static MyStream& endl(MyStream& stream)
    {
        // print a new line
        std::cout << std::endl;

        // do other stuff with the stream
        // std::cout, for example, will flush the stream
        stream << "Called MyStream::endl!" << std::endl;

        return stream;
    }

    // this is the type of std::cout
    typedef std::basic_ostream<char, std::char_traits<char> > CoutType;

    // this is the function signature of std::endl
    typedef CoutType& (*StandardEndLine)(CoutType&);

    // define an operator<< to take in std::endl
    MyStream& operator<<(StandardEndLine manip)
    {
        // call the function, but we cannot return it's value
        manip(std::cout);

        return *this;
    }
};

int main(void)
{
    MyStream stream;

    stream << 10 << " faces.";
    stream << MyStream::endl;
    stream << std::endl;

    return 0;
}

うまくいけば、これはこれらのものがどのように機能するかについてより良い考えをあなたに与えるでしょう。

79
GManNickG

問題は、演算子std::endlと同様に、<<が関数テンプレートであることです。だからあなたが書くとき:

my_stream << endl;

コンパイラーは、演算子とendlのテンプレートパラメーターを推定する必要があります。これは不可能です。

そのため、マニピュレータを操作するには、追加のテンプレートではない、演算子<<のオーバーロードを記述する必要があります。プロトタイプは次のようになります。

UIStream& operator<<(UIStream& os, std::ostream& (*pf)(std::ostream&));

(他に2つあり、std::ostreamstd::basic_ios<char>std::ios_baseで置き換えます。すべてのマニピュレータを許可する場合は、これらも指定する必要があります)およびその実装は、テンプレート。実際、非常によく似ているため、次のような実装にテンプレートを使用できます。

typedef std::ostream& (*ostream_manipulator)(std::ostream&);
UIStream& operator<<(UIStream& os, ostream_manipulator pf)
{
   return operator<< <ostream_manipulator> (os, pf);
}

多くの場合、カスタムstreambufを作成する最後のメモは、多くの場合、使用しているテクニックへの適用を達成するためのより良い方法です。

36
AProgrammer

私はこれを私の問題を解決するために行いました、これが私のコードの一部です:

    template<typename T> 
    CFileLogger &operator <<(const T value)
    {
        (*this).logFile << value;
        return *this;
    }
    CFileLogger &operator <<(std::ostream& (*os)(std::ostream&))
    {
        (*this).logFile << os;
        return *this;
    }

Main.cpp

int main(){

    CFileLogger log();    
    log << "[WARNINGS] " << 10 << std::endl;
    log << "[ERRORS] " << 2 << std::endl;
    ...
}

私はここでリファレンスを得ました http://www.cplusplus.com/forum/general/49590/

これが誰かを助けることを願っています。

7

IOStreamsを拡張するより良い方法については here を参照してください。 (少し時代遅れであり、VC 6に合わせて調整されているため、塩の粒と一緒に使用する必要があります)

重要なのは、ファンクタを機能させるには(そしてendlは "\ n"を出力し、フラッシュすることがファンクタである)、完全なostreamインターフェイスを実装する必要があるということです。

4
EFraim

stdストリームは、仮想メソッドがないため、サブクラス化するようには設計されていません。そのため、これで行き過ぎることはないと思います。 std :: ostreamを集約して作業を行うこともできます。

endlを機能させるには、endlなどのマニピュレーターを処理する方法である関数へのポインターを受け取るoperator<<のバージョンを実装する必要があります。

UStream& operator<<( UStream&, UStream& (*f)( UStream& ) );

または

UStream& UStream::operator<<( UStream& (*f)( UStream& ) );

ここで、std::endlはstd :: basic_ostreamへの参照を受け取って返す関数であるため、ストリームで直接動作しないため、集約されたstd::endlstd::iostreamバージョンを呼び出す独自のバージョンを作成する必要があります。

編集:GManの回答の方が良いようです。彼はstd::endlも動作します!

3
Troubadour

受け入れられた回答に加えて、C++ 11では、型のoperator<<をオーバーロードすることが可能です。

decltype(std::endl<char, std::char_traits<char>>)
1
AlwaysLearning