Std:: endl неизвестного типа при перегрузке оператора <<

I перегруженный оператор < <

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

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

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

my_stream << endl;

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

ошибка C2678: двоичная '< <: оператор не найден, который принимает левый операнд типа UIStream (или нет приемлемого преобразования)

Какова работа для работы my_stream << endl?

Ответ 1

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 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 value
        manip(std::cout);

        return *this;
    }
};

int main(void)
{
    MyStream stream;

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

    return 0;
}

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

Ответ 2

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

Ответ 3

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

    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

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

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

Ответ 5

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

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

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

или

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

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

Изменить: выглядит лучше GMan ответ лучше. Он также работает std::endl!

Ответ 6

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

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