重载 << 运算符:带有结构向量的结构
Overloading << operator: struct with vector of structs
- 重载 << 向量有效。
- 为自定义结构重载 << 有效。
- 该组合也有效。
但是,如果我在具有结构向量的结构上使用<<运算符,编译将失败。
我编了一个小例子来展示这个问题:
#include <iostream>
#include <ostream>
#include <vector>
template <typename T>
std::ostream& operator<<(std::ostream& out, const std::vector<T>& v) {
out << "[";
for (auto it = v.begin(); it != v.end(); ++it) {
out << *it;
if (std::next(it) != v.end()) {
out << ", ";
}
}
out << "]";
return out;
}
namespace xyz {
struct Item {
int a;
int b;
};
struct Aggregation {
std::vector<Item> items;
};
std::ostream& operator<<(std::ostream& out, const Item& item) {
out << "Item(" << "a = " << item.a << ", " << "b = " << item.b << ")";
return out;
}
std::ostream& operator<<(std::ostream& out, const Aggregation& agg) {
out << "Aggregation(" << "items = " << agg.items << ")";
return out;
}
} // namespace xyz
int main() {
xyz::Aggregation agg;
agg.items.emplace_back(xyz::Item{1, 2});
agg.items.emplace_back(xyz::Item{3, 4});
std::cout << agg.items << std::endl; // works: [Item(a = 1, b = 2), Item(a = 3, b = 4)]
std::cout << agg << std::endl; // fails, expected: Aggregation(items = [Item(a = 1, b = 2), Item(a = 3, b = 4))
}
Link 到编译器资源管理器:https://godbolt.org/z/a8dccf
<source>: In function 'std::ostream& xyz::operator<<(std::ostream&, const xyz::Aggregation&)':
<source>:35:41: error: no match for 'operator<<' (operand types are 'std::basic_ostream<char>' and 'const std::vector<xyz::Item>')
35 | out << "Aggregation(" << "items = " << agg.items << ")";
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^~ ~~~~~~~~~
| | |
| | const std::vector<xyz::Item>
| std::basic_ostream<char>
In file included from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/iostream:39,
from <source>:1:
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/ostream:108:7: note: candidate: 'std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ostream<_CharT, _Traits>::__ostream_type& (*)(std::basic_ostream<_CharT, _Traits>::__ostream_type&)) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]'
108 | operator<<(__ostream_type& (*__pf)(__ostream_type&))
| ^~~~~~~~
我做错了什么?
在main
函数中,当你写这行时:
std::cout << agg.items << std::endl;
编译器将在全局命名空间中查找 operator<<
的所有重载。通过重载决策选择正确的重载,因此调用有效。
当你在这里写类似的代码时
std::ostream& operator<<(std::ostream& out, const Aggregation& agg) {
out << "Aggregation(" << "items = " << agg.items << ")";
return out;
}
由于此代码位于命名空间 xyz
中,编译器将首先在命名空间 xyz
中查找 operator<<
的重载。一旦发现任何过载,它将停止寻找额外的过载。但是,由于您想要的实际 operator<<
不在命名空间 xyz
中,因此重载解析失败,您会收到错误消息。
解决方法是简单地将 operator<<
和 vector<T>
移动到命名空间 xyz
.
这是一个demo。
如果你真的想要一个 operator<<
可以从全局范围和命名空间 xyz
访问任何类型的 vector
,那么你可以在正如您在问题中所做的那样,全球范围。然后只需将运算符带入 xyz
,或者最好带入命名空间 xyz
中您需要它们的特定函数,如下所示:
namespace xyz
{
// using ::operator<<; // if you want all of `xyz` to see the global overload
std::ostream& operator<<(std::ostream& out, const Aggregation& agg)
{
using ::operator<<; // if you only want the global overload to be visible in this function
out << "Aggregation(" << "items = " << agg.items << ")";
return out;
}
// ...
}
这里的 demo 展示了如何流式传输 vector<int>
以及 vector<xyz::Item>
。
感谢@NathanPierson 指出 using
声明可以在需要它的函数中使用,而不是污染整个命名空间 xyz
.
我 运行 再次处理 fmt 库 (https://github.com/fmtlib/fmt/issues/2093) 的类似问题。另一个可行的解决方案似乎是将 std 容器的 operator<<
重载直接添加到命名空间 std:
namespace std {
template <typename T>
std::ostream& operator<<(std::ostream& out, const std::vector<T>& v) {
out << "[";
for (auto it = v.begin(); it != v.end(); ++it) {
out << *it;
if (std::next(it) != v.end()) {
out << ", ";
}
}
out << "]";
return out;
}
} // namespace std
Link 到编译器资源管理器:https://godbolt.org/z/o7c9WP
不过,我对向命名空间 std 添加一些内容感到很不爽。对此有什么想法吗?
- 重载 << 向量有效。
- 为自定义结构重载 << 有效。
- 该组合也有效。
但是,如果我在具有结构向量的结构上使用<<运算符,编译将失败。 我编了一个小例子来展示这个问题:
#include <iostream>
#include <ostream>
#include <vector>
template <typename T>
std::ostream& operator<<(std::ostream& out, const std::vector<T>& v) {
out << "[";
for (auto it = v.begin(); it != v.end(); ++it) {
out << *it;
if (std::next(it) != v.end()) {
out << ", ";
}
}
out << "]";
return out;
}
namespace xyz {
struct Item {
int a;
int b;
};
struct Aggregation {
std::vector<Item> items;
};
std::ostream& operator<<(std::ostream& out, const Item& item) {
out << "Item(" << "a = " << item.a << ", " << "b = " << item.b << ")";
return out;
}
std::ostream& operator<<(std::ostream& out, const Aggregation& agg) {
out << "Aggregation(" << "items = " << agg.items << ")";
return out;
}
} // namespace xyz
int main() {
xyz::Aggregation agg;
agg.items.emplace_back(xyz::Item{1, 2});
agg.items.emplace_back(xyz::Item{3, 4});
std::cout << agg.items << std::endl; // works: [Item(a = 1, b = 2), Item(a = 3, b = 4)]
std::cout << agg << std::endl; // fails, expected: Aggregation(items = [Item(a = 1, b = 2), Item(a = 3, b = 4))
}
Link 到编译器资源管理器:https://godbolt.org/z/a8dccf
<source>: In function 'std::ostream& xyz::operator<<(std::ostream&, const xyz::Aggregation&)':
<source>:35:41: error: no match for 'operator<<' (operand types are 'std::basic_ostream<char>' and 'const std::vector<xyz::Item>')
35 | out << "Aggregation(" << "items = " << agg.items << ")";
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^~ ~~~~~~~~~
| | |
| | const std::vector<xyz::Item>
| std::basic_ostream<char>
In file included from /opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/iostream:39,
from <source>:1:
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/ostream:108:7: note: candidate: 'std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ostream<_CharT, _Traits>::__ostream_type& (*)(std::basic_ostream<_CharT, _Traits>::__ostream_type&)) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]'
108 | operator<<(__ostream_type& (*__pf)(__ostream_type&))
| ^~~~~~~~
我做错了什么?
在main
函数中,当你写这行时:
std::cout << agg.items << std::endl;
编译器将在全局命名空间中查找 operator<<
的所有重载。通过重载决策选择正确的重载,因此调用有效。
当你在这里写类似的代码时
std::ostream& operator<<(std::ostream& out, const Aggregation& agg) {
out << "Aggregation(" << "items = " << agg.items << ")";
return out;
}
由于此代码位于命名空间 xyz
中,编译器将首先在命名空间 xyz
中查找 operator<<
的重载。一旦发现任何过载,它将停止寻找额外的过载。但是,由于您想要的实际 operator<<
不在命名空间 xyz
中,因此重载解析失败,您会收到错误消息。
解决方法是简单地将 operator<<
和 vector<T>
移动到命名空间 xyz
.
这是一个demo。
如果你真的想要一个 operator<<
可以从全局范围和命名空间 xyz
访问任何类型的 vector
,那么你可以在正如您在问题中所做的那样,全球范围。然后只需将运算符带入 xyz
,或者最好带入命名空间 xyz
中您需要它们的特定函数,如下所示:
namespace xyz
{
// using ::operator<<; // if you want all of `xyz` to see the global overload
std::ostream& operator<<(std::ostream& out, const Aggregation& agg)
{
using ::operator<<; // if you only want the global overload to be visible in this function
out << "Aggregation(" << "items = " << agg.items << ")";
return out;
}
// ...
}
这里的 demo 展示了如何流式传输 vector<int>
以及 vector<xyz::Item>
。
感谢@NathanPierson 指出 using
声明可以在需要它的函数中使用,而不是污染整个命名空间 xyz
.
我 运行 再次处理 fmt 库 (https://github.com/fmtlib/fmt/issues/2093) 的类似问题。另一个可行的解决方案似乎是将 std 容器的 operator<<
重载直接添加到命名空间 std:
namespace std {
template <typename T>
std::ostream& operator<<(std::ostream& out, const std::vector<T>& v) {
out << "[";
for (auto it = v.begin(); it != v.end(); ++it) {
out << *it;
if (std::next(it) != v.end()) {
out << ", ";
}
}
out << "]";
return out;
}
} // namespace std
Link 到编译器资源管理器:https://godbolt.org/z/o7c9WP
不过,我对向命名空间 std 添加一些内容感到很不爽。对此有什么想法吗?