std::ostream&operator<<类型扣除
std::ostream& operator<< type deduction
我有一个类似的模板函数
#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();
}
问题是,如果我定义了两个friend方法,它就不会编译。如果我只定义一个方法并注释其中一个迭代器列表打印,它就可以工作了。
我得到的错误是
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> >]’
有人能告诉我这里发生了什么事吗?
好吧,代码在clang++中为我编译并运行。无法在此计算机上尝试使用g++。
编辑:实际上,它也使用g++编译,这是有意义的,因为您只在全局命名空间中的main中使用operator<<
。我假设您的实际代码不同\编辑
但我很熟悉中的"ostream lvalue不能绑定到ostream&&"错误
如何解释。在ostreams
和任何std
类之间提供operator<<
存在问题(如您的示例中的list
,但我在vector
中发现了它)
大多数情况下,它是有效的,但当从命名空间(如my
命名空间)调用运算符时,它会中断。
为什么?因为"我在哪里查找此运算符<<成员"?看,可能有很多运算符<lt;在ostream和list之间-每个都在不同的命名空间中。那么编译器在哪里查找它呢?
它在其操作数上查找每个操作数的名称空间(在您的情况下,两者都来自std
)。并且有时在调用者的命名空间中(在您的情况下是my
)。
我说"有时"是因为根据标准,它不应该,但g++无论如何都会这样做。clang++没有,而是在全局命名空间中查找(因此它对我有效)
理想情况下,您应该将运算符<lt;在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;
它并不漂亮,但这就是我所发现的。。。
我在全局命名空间中声明的以下运算符也有同样的问题:
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引入了一个catch-all std::operator <<
模板,<ostream>
中的注释将其描述为"右值流的通用插入器"。编译器找不到全局::operator <<
模板,因为依赖于参数的查找首先找到std::operator <<
模板。
一个简单而正确的解决方案是通过using
声明将全局运算符带入本地作用域:
std::ostream & operator << (std::ostream &os, const Foo &foo) {
using ::operator <<;
return os << foo.items; // OK
}
错误取决于标准库的版本,请参阅@matt-whitlock的答案。
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;
- ArduinoJson 6.15.2:JsonObject没有命名类型
- 防止主数据类型C++的隐式转换
- 大量序列中核苷酸类型的快速计数
- 如何从C++中的依赖类型中获得它所依赖的类型
- 有关插入适配器的错误。[错误]请求从 'back_insert_iterator<vector<>>' 类型转换为非标量类型
- 是否可以初始化不可复制类型的成员变量(或基类)
- 如何获取std::result_of函数的返回类型
- 从父命名空间重载类型
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 我想将一个对T类型的非常量左值引用绑定到一个T类型的临时值
- Openssl 1.1.1d无效使用不完整的类型"struct dsa_st"
- 访问者访问变体并返回不同类型时出错
- 在VS2010-VS2015下编译时,如何使用decltype作为较大类型表达式的LHS
- 处理小于cpu数据总线的数据类型.(c++转换为机器代码)
- C++ 雷神库 - 使用资源加载器类时出现问题(不命名类型)
- 模板元程序查找相似的连续类型名称
- 请解释这句话(cout<<1+int((a<b)^((b-a)&1) )<<endl
- 是否可以从int转换为enum类类型
- eigen :: llt&lt;eigen :: matrixxd&gt;具有不完整的类型
- std::pair的默认构造函数<>将基本类型(int等)设置为零