문제

I have a template function like this one

#include <list>
#include <iostream>

template<typename T>
std::ostream& operator<<(std::ostream& out, const std::list<T>& list){
    out << "[";
    if(!list.empty()){
        typename std::list<T>::const_iterator it = list.cbegin();
        out << *it;
        for (++it; it != list.cend(); ++it){
            out << ", ";
            out << *it;
        }
    }
    out << "]";
    return out;
}

And some template class with nested classes

namespace my{

    template<
        typename T,
        typename U = size_t
    >

    class graph{

    public:
        typedef T dist_t;
        typedef U node_t;

        class node_pt;
        typedef struct arc_t{
            node_pt* from = nullptr;
            node_pt* to = nullptr;
            dist_t weight;
        } arc_t;
        typedef struct arc_pt{
            arc_t arc;
        } arc_pt;
        typedef struct node_pt{
            node_t node;
        } node_pt;

        class arc_iterator{
        public:
            arc_pt* pt = nullptr;
        public:
            arc_pt* operator->() const{
                return pt;
            }

            friend std::ostream& operator<< (std::ostream &out, const arc_iterator& it) {
                out << "(" << it->arc.from->node << "," << it->arc.to->node << "," << it->arc.weight << ")";
                return out;
            }
        };

        class node_iterator{
        public:
            node_pt* pt = nullptr;

        public:

            node_t operator *() const{
                return pt->node;
            }

            friend std::ostream& operator<< (std::ostream &out, const node_iterator& it) {
                out << *it;
                return out;
            }
        };

    };
}

Some code to reproduce the problem

namespace my{
    namespace test{
        void run(){     
            typedef my::graph<size_t> graph_t;
            std::list<graph_t::node_t> l1;
            std::list<graph_t::dist_t> l2;
            std::list<graph_t::node_iterator> l3;
            std::list<graph_t::arc_iterator> l4;

            std::cout << l1 << std::endl;
            std::cout << l2 << std::endl;
            std::cout << l3 << std::endl;
            std::cout << l4 << std::endl;
        }
    }
}


int main(){
    my::test::run();
}

The problem is it doesn't compile if I define the two friend methods. If I only define one method and comment one of the iterator list printing it works.

The error I'm getting is

src/OTest_Graph.cpp: In member function ‘virtual void my::test::TestGraph::run()’:
src/OTest_Graph.cpp:59:53: error: cannot bind ‘std::basic_ostream<char>’ lvalue to ‘std::basic_ostream<char>&&’
In file included from /usr/include/c++/4.7/iostream:40:0,
                 from h/OTest_Graph.h:4,
                 from src/OTest_Graph.cpp:1:
/usr/include/c++/4.7/ostream:600:5: error:   initializing argument 1 of ‘std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char; _Traits = std::char_traits<char>; _Tp = std::list<my::graph<long unsigned int>::node_iterator, std::allocator<my::graph<long unsigned int>::node_iterator> >]’

Can anyone tell me what's going on here?

도움이 되었습니까?

해결책 3

The error depends on the version of the standard library, see @matt-whitlock's answer.

A solution for g++ 4.7 :

Instead of

std::cout << l1 << std::endl;
std::cout << l2 << std::endl;
std::cout << l3 << std::endl;
std::cout << l4 << std::endl;

use

::operator<<(std::cout, l1) << std::endl;
::operator<<(std::cout, l2) << std::endl;
::operator<<(std::cout, l3) << std::endl;
::operator<<(std::cout, l4) << std::endl;

다른 팁

Well, the code compiles and runs for me in clang++. Can't try with g++ on this computer.

Edit: Actually, it compiles with g++ as well, which makes sense because you only use the operator<< in the main which is in the global namespace. I assume your actual code is different \Edit

But I'm familiar with the "ostream lvalue can't bind to ostream&&" error

How to explain. There is a problem in providing operator<< between ostreams and any std class (like list in your example, but I found it with vector)

Mostly it works, but when the operator is called from a namespace (like your my namespace) it breaks.

Why? Because "where do I look for this operator<< member"? See, there might be a lot of operator<< between ostreams and lists - each in a different namespace. So where does the compiler look for it?

It looks in the namespaces of each on its operands (in your case - both are from std). And sometimes in the namespace of the caller (which in your case is my).

I say "sometimes" because according to the standard it shouldn't, but g++ does it anyway. clang++ doesn't - but looks in the global namespace instead (hence why it worked for me)

Ideally, you'd want to put the operator<< inside the std namespace (try it - it will work). BUT - that is against the standard. You are not allowed to do that. You can put it in the my namespace and it should work find in g++, but not in other compilers.

It's a problem. I "solved" it by creating a wrapper - a class that exists in my own namespace and only holds a reference to the std class - and can be printed.

template<class T> struct OutList<T>{
  const std::list<T> &lst;
  OutList(const std::list &l):lst(l){}
};

template<class T> OutList<T> outlist(const std::list<T> &lst){return OutList<T>(lst);}

std::ostream &operator<<(std::stream &out,const OutList<T> &lst){...}

....
std::cout << "list= "<<outlist(list)<<std::endl;

It isn't pretty, but that's all I found...

I had the same problem with the following operator, declared in the global namespace:

template <typename T>
std::ostream & operator << (std::ostream &os, const std::vector<T> &vector);

…when called from a function declared in a named namespace:

std::ostream & operator << (std::ostream &os, const Foo &foo) {
    return os << foo.items;  // error
}

…where Foo::items is a std::vector.

g++ gives the infamous error:

error: cannot bind 'std::basic_ostream<char>' lvalue to 'std::basic_ostream<char>&&'

The error arises because C++11 introduced a catch-all std::operator << template, which the comment in <ostream> describes as a "generic inserter for rvalue stream." The compiler does not find the global ::operator << template because argument-dependent lookup finds the std::operator << template first.

A simple and correct fix is to bring the global operator into the local scope by a using declaration:

std::ostream & operator << (std::ostream &os, const Foo &foo) {
    using ::operator <<;
    return os << foo.items;  // OK
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top