我正在尝试为任何可迭代容器编写模板运算符。得到一个奇怪的错误:
#include <iostream>
template <typename C>
std::ostream& operator<<(std::ostream& os, const C& c) {
os << "[";
for (const auto& v : c) {
os << v << " ";
}
os << "]";
return os;
}
vec.cc:5:6:错误:使用重载运算符'&lt;&lt;'是暧昧的(有 操作数类型'std :: ostream'(又名'basic_ostream')和'const char [2]')os&lt;&lt; “[”; ~~ ^ ~~~
为什么会出现这个错误?我如何实现我的目标?
答案 0 :(得分:9)
添加
Pool
与template <typename C>
std::ostream& operator<<(std::ostream& os, const C& c) {
os << "[";
for (const auto& v : c) {
os << v << " ";
}
os << "]";
return os;
}
的其他global overloads冲突。
要解决此问题,我们可以使用
将模板限制为任何向量而不是任何类型operator <<
答案 1 :(得分:4)
在这一行:
os << "[";
编译器找到两个有效函数:STL和你的。
为了解决冲突,您需要在模板声明中更具体一些:
template <typename C>
std::ostream& operator<<(std::ostream& os, const std::vector<C>& c) {
...
将此扩展到容器通常需要与std::enable_if<>
进行一些讨论,这可能会让您更加困惑。我建议您只为要支持的每种容器添加一个重载。
编辑另外,覆盖ostream << T
,对于您不拥有的类型通常是一个坏主意,因为它最终会导致冲突。
答案 2 :(得分:3)
您收到此错误的原因是您的功能与左侧<<
的{{1}}的每次通话都匹配。
std::ostream&
编写template <typename C>
std::ostream& operator<<(std::ostream& os, const C& c) {
os << "[";
for (const auto& v : c) {
os << v << " ";
}
os << "]";
return os;
}
时,编译器会找到多个要调用的os << "["
函数;你的其中之一就是其中之一。通过添加模板化的全局operator<<
,您可以截取每次operator<<
的调用。
最简洁的方法是定义一个新功能,比如operator<<
:
print_collection
如果确实想要定义template <typename C>
void print_collection(std::ostream& os, const C& c) {
os << "[";
for (const auto& v : c) {
os << v << " ";
}
os << "]";
}
,这会变得更加棘手。你可以这样做:
operator<<
但是,如果标准库决定为template <typename C>
std::ostream& operator<<(std::ostream& os, const std::vector<C>& c) {
os << "[";
for (const auto& v : c) {
os << v << " ";
}
os << "]";
return os;
}
添加自己的operator<<
,则代码将会中断。
我强烈建议您如果要添加此类std::vector
,请按照自己的类型进行操作。像这样:
operator<<
然后你可以像这样使用它:
template <typename Iter>
class Range {
Iter begin_;
Iter end_;
public:
Range() = default;
Range(Iter begin, Iter end)
: begin_{ begin }
, end_{ end }
{}
auto begin() const { return begin_; }
auto end() const { return end_; }
};
template <typename Iter>
auto range(Iter begin, Iter end) {
return Range<Iter>{ begin, end };
}
template <typename C>
auto range(const C& collection) {
return range(std::begin(collection), std::end(collection));
}
template <typename Iter>
std::ostream& operator<<(std::ostream& os, const Range<Iter>& range) {
os << "[";
for (const auto& v : range) {
os << v << " ";
}
os << "]";
return os;
}
答案 3 :(得分:3)
发生错误是因为您的operator<<
与标准库中的许多其他重载相匹配。在你的情况下,重载意味着const char[2]
。
如果您希望这适用于任何可迭代容器,一种方法是约束它以检查其上begin()
和end()
方法的有效性
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
template <typename Container,
std::enable_if_t<std::is_same<
decltype(std::declval<Container>().begin()),
decltype(std::declval<Container>().begin())>::value>* = nullptr>
std::ostream& operator<<(std::ostream& os, const Container& container) {
os << "[";
for (const auto& ele : container) {
os << ele << " ";
}
os << "]";
return os;
}
int main() {
auto vec = std::vector<int>{1, 2, 3};
cout << vec << endl;
}
答案 4 :(得分:2)
我相信你对运营商的定义&lt;&lt;使用为C ++流定义的标准运算符导致歧义。
我尝试使用一些SFINAE技术来改变原型,例如:
template <typename C>
std::ostream& operator<<(std::ostream& os, const C& c, C::const_iterator fakeVar = c.begin() ) {
os << "[";
for (const auto& v : c) {
os << v << " ";
}
os << "]";
return os;
}
只有在可以编译的情况下才能使你的模板适合
答案 5 :(得分:0)
正如其他人所解释的那样,对于os << "["
,您的运营商会引入歧义。
我不知道这是不是一个好主意,但我建议使用SFINAE使您的运营商仅支持begin()
类型。
我的意思是
template <typename C>
auto operator<<(std::ostream& os, const C& c)
-> decltype( c.begin(), os ) {
os << "[";
for (const auto & v : c) {
os << v << " ";
}
os << "]";
return os;
}
答案 6 :(得分:0)
模糊性源于您的模板重载了operator<<
的{{1}},并且可以使用多个定义。
实际上,例如,标准库定义了:
std::ostream
您的函数将与模板替换std::ostream& operator<<(std::ostream&, const char*);
匹配。
为了解决这种歧义,您可以采用另一种设计选择(例如,避免重载typename C = const char*
)或解决专门模板功能的歧义。
在这种情况下,许多其他答案都可能是好的,我建议你另一种选择(非常简洁,但有一些限制。见下文)。
由于您正在尝试打印容器,因此容器可能是模板类(operator<<
,std::vector<T>
等...)。因此,可以稍微修改您的函数,以便只处理模板类的类型。
std::list<T>
请注意,由于可变参数模板,代码将需要 C ++ 11 。
Here一个例子,表明歧义已经解决。
请注意,该解决方案的限制是,它仅适用于作为模板类的容器。它不适用于template <template <typename, typename...> class Container, typename T,
typename... Args>
std::ostream& operator<<(std::ostream& os, const Container<T, Args...>& c) {
os << "[";
for (const auto& v : c) {
os << v << " ";
}
os << "]";
return os;
}
,因为数组的结构是different。