std :: endl имеет неизвестный тип при перегрузке оператора << - PullRequest
54 голосов
/ 16 июля 2009

Я перегружен оператором << </p>

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

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

Работает, но:

my_stream << endl;

Дает ошибку компиляции:

ошибка C2678: двоичный файл «<<»: не найден оператор, который принимает левый операнд типа «UIStream» (или нет допустимого преобразования) </p>

Какой способ заставить my_stream << endl работать?

Ответы [ 6 ]

74 голосов
/ 16 июля 2009

std::endl является функцией, и std::cout использует ее, реализуя operator<<, чтобы получить указатель на функцию с такой же сигнатурой, что и 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;
}

Надеюсь, это даст вам лучшее представление о том, как эти вещи работают.

35 голосов
/ 16 июля 2009

Проблема в том, что std::endl - это шаблон функции, так как ваш оператор << является. Поэтому, когда вы пишете:

my_stream << endl;

вам понравится компилятор для определения параметров шаблона для оператора а также для endl. Это невозможно.

Таким образом, вы должны написать дополнительные, не шаблонные, перегрузки оператора << в работать с манипуляторами. Их прототип будет выглядеть так:

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

(есть еще два, заменив std::ostream на std::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 часто лучший способ достигните того, чего пытаетесь достичь, применяя технику, которую вы используете.

6 голосов
/ 09 января 2016

Я сделал это, чтобы решить мою проблему, вот часть моего кода:

    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/

Надеюсь, это кому-нибудь поможет.

4 голосов
/ 16 июля 2009

См. здесь , чтобы узнать о лучших способах расширения потоков IOS. (Немного устарело и специально для VC 6, так что вам придется взять его с крошкой соли)

Дело в том, что для работы функторов (и endl, который выводит "\ n" и флеширует - это функтор), вам необходимо реализовать полный интерфейс ostream.

3 голосов
/ 16 июля 2009

Потоки std не предназначены для использования в подклассах, поскольку у них нет виртуальных методов, поэтому я не думаю, что вы слишком далеко зайдете. Вы можете попробовать агрегировать std :: ostream для выполнения этой работы.

Чтобы заставить endl работать, вам необходимо внедрить версию operator<<, которая принимает указатель на функцию, так как именно так обрабатываются манипуляторы, такие как endl, т.е.

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

или

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

Теперь std::endl - это функция, которая принимает и возвращает ссылку на std :: basic_ostream, поэтому она не будет работать напрямую с вашим потоком, поэтому вам нужно будет создать собственную версию, которая обращается к std::endl версия в вашем агрегированном std::iostream.

Редактировать: Похоже, ответ GMan лучше. Он тоже std::endl работает!

1 голос
/ 11 января 2016

В дополнение к принятому ответу, в C ++ 11 возможно перегрузить operator<< для типа:

decltype(std::endl<char, std::char_traits<char>>)
...