станд :: ostream & амп; Оператор & л; & л; тип вычета

У меня есть функция шаблона, как этот

#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;
}

И некоторый шаблон класса с вложенными классами

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;
}
};

};
}

Некоторый код для воспроизведения проблемы

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();
}

Проблема в том, что он не компилируется, если я определяю два метода-друга. Если я определю только один метод и прокомментирую один из списков итераторов, он будет работать.

Я получаю ошибку

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> >]’

Кто-нибудь может сказать мне, что здесь происходит?

2

Решение

Ну, код компилируется и запускается для меня в Clang ++. Не могу попробовать с g ++ на этом компьютере.

Редактировать: На самом деле, он также компилируется с g ++, что имеет смысл, потому что вы используете только operator<< в основном, который находится в глобальном пространстве имен. Я предполагаю, что ваш фактический код отличается \Редактировать

Но я знаком с «ostream lvalue не может связываться с ostream&&» ошибка

Как объяснить. Существует проблема в предоставлении operator<< между ostreams и любой std класс (как list в вашем примере, но я нашел это с vector)

В основном это работает, но когда оператор вызывается из пространства имен (как ваш my пространство имен) это ломается.

Зачем? Потому что «где искать этого оператора<< член «? Видите, там может быть много оператора<< между ostreams и списками — каждый в различном пространстве имен. Так, где компилятор ищет это?

Он смотрит в пространствах имен каждого на свои операнды (в вашем случае — оба от std). А также иногда в пространстве имен абонента (который в вашем случае my).

Я говорю «иногда», потому что в соответствии со стандартом этого не должно быть, но g ++ все равно делает это. clang ++ этого не делает, но вместо этого ищет в глобальном пространстве имен (поэтому и работает для меня)

В идеале вы бы хотели поставить оператора<< внутри std Пространство имен (попробуйте — это будет работать). НО — это против стандарта. Вам не разрешено это делать. Вы можете положить его в my Пространство имен и это должно работать найти в g ++, но не в других компиляторах.

Это проблема. Я «решил» это, создав оболочку — класс, который существует в моем собственном пространстве имен и содержит только ссылку на std класс — и может быть напечатан.

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;

Это не красиво, но это все, что я нашел …

4

Другие решения

У меня была такая же проблема со следующим оператором, объявленным в глобальном пространстве имен:

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

… При вызове из функции, объявленной в именованном пространстве имен:

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

…где Foo::items это std::vector,

G ++ дает печально известную ошибку:

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

Ошибка возникает из-за того, что в C ++ 11 введено универсальное решение std::operator << шаблон, в котором комментарий <ostream> описывает как «универсальный вставщик для потока rvalue». Компилятор не находит глобальный ::operator << шаблон, потому что аргумент-зависимый поиск находит std::operator << шаблон первый.

Простое и правильное решение — привести глобальный оператор в локальную область using объявление:

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

Ошибка зависит от версии стандартной библиотеки, см. @ ответ Мэтта-Уитлока.

Решение для g++ 4.7 :

Вместо

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

использование

::operator<<(std::cout, l1) << std::endl;
::operator<<(std::cout, l2) << std::endl;
::operator<<(std::cout, l3) << std::endl;
::operator<<(std::cout, l4) << std::endl;
1
По вопросам рекламы [email protected]