我想知道为什么以下内容无法编译:
#include <vector>
#include <ostream>
#include <iterator>
#include <algorithm>
#include <iostream>
template <template <typename...> class Container, class T>
std::ostream& operator<<(std::ostream& oss, const Container<T>& c) {
oss << "[";
std::copy(std::cbegin(c), std::prev(std::cend(c)), std::ostream_iterator<T>(oss, ","));
return oss << (*std::crbegin(c)) << "]";
}
auto main() -> int {
std::vector<std::vector<unsigned>> data(5);
std::cout << data << std::endl;
return 0;
}
http://coliru.stacked-crooked.com/a/431617423f92ba4e
当我执行以下一项操作时,它可以正常编译:
std::copy
删除行std::vector<unsigned> data(5)
)。引起错误的std::copy
是什么?
使用clion进行调试,这是在crbegin
行中打印的嵌套矢量的类型:
答案 0 :(得分:3)
因为operator<<
实体看不到您的std
。
注意std::ostream_iterator<T>
输出值as if through operator<<
,并根据[temp.dep.res]/1:
在解析从属名称时,来自以下来源的名称是 考虑过:
- 在模板定义时可见的声明。
- 来自实例化上下文([temp.point])中与函数参数类型相关联的名称空间的声明 并从定义上下文开始。
...您的operator<<
在std::ostream_iterator<T>
的定义点或命名空间std
中都不可见,因此{{1}中使用的operator<<
}无法正确解析。
答案 1 :(得分:1)
您的operator<<
对std::ostream_iterator
不可见,因此它无法在输入容器的元素上调用operator<<
。只需使用手动循环,即可按预期工作。
此外,当容器为空时,std::prev(std::cend(c))
和*(c.crbegin())
是 undefined ,所以要当心。
此外,std::vector
(与大多数其他标准容器一样)具有多个模板参数,因此请在操作员的模板参数中使用typename... Ts
而不是class T
。
尝试一下:
#include <vector>
#include <iterator>
#include <iostream>
template < template<typename...> class Container, typename... Ts>
std::ostream& operator<<(std::ostream& oss, const Container<Ts...>& c) {
oss << "[";
if (!c.empty()) { // use std::empty() in C++17 and later
auto last = std::prev(std::cend(c));
/*
using value_type = typename Container<Ts...>::value_type;
std::copy(std::cbegin(c), last, std::ostream_iterator<value_type>(oss, ","));
*/
for(auto iter = std::cbegin(c); iter != last; ++iter)
oss << *iter << ",";
oss << *last;
}
return oss << "]";
}
int main() {
std::vector<std::vector<unsigned>> data(5);
std::cout << data << std::endl;
return 0;
}
输出
[[],[],[],[],[]]
答案 2 :(得分:0)
这是一个实验,可以引导您找到答案:
#include <vector>
#include <ostream>
#include <iterator>
#include <algorithm>
#include <iostream>
namespace std {
template <template <typename...> class Container, class T>
std::ostream& operator<<(std::ostream& oss, const Container<T>& c) {
oss << "[";
std::copy(std::cbegin(c), std::prev(std::cend(c)), std::ostream_iterator<T>(oss, ","));
return oss << (*std::crbegin(c)) << "]";
}
}
int main() {
std::vector<unsigned> t{ 1, 2, 3 };
std::vector<std::vector<unsigned>> data(5, t);
std::cout << data << std::endl;
return 0;
}
旁注:如果不定义t
,它将进行编译(即,您的operator<<
将在需要时找到),但是如果您尝试运行它,它将崩溃—尝试使用在空容器上的std::prev(std::cend(c))
收尾不好。
当然,这确实违反了标准要求(不允许在这样的命名空间std中定义operator<<
),但是至少对于典型的编译器而言,现在可以找到名称,因此代码将编译。