有一天你想对容器重载运算符, 但是却出现了神秘问题
在日复一日地对容器书写
for.cppview raw1 2 3 4
| for (auto it = container.begin(); it != container.end();) { std::cout << *it++; if (it != container.end()) std::cout << ", "; }
|
后, 你终于受不了了, 于是你决定写一个重载来一劳永逸地解决这个问题
overload.cppview raw1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #include <iostream> #include <set> #include <vector>
template <class Container, decltype(std::declval<Container>().begin(), std::declval<Container>().end(), 0) * = nullptr> std::ostream &operator<<(std::ostream &os, const Container &container) { if (container.begin() == container.end()) return os << "[]"; os << '['; for (auto it = container.begin(); it != container.end();) { std::cout << *it++; if (it != container.end()) std::cout << ", "; } os << ']'; return os; }
int main() { std::vector<int> v{1, 1, 4, 5, 1, 4}; std::cout << v << std::endl; std::set<int> s{1, 1, 4, 5, 1, 4}; std::cout << s << std::endl; return 0; }
|
一切看起来都是那么美好, 直到你尝试输出一个字符串. 在你输出字符串时,
编译器拒绝了你的代码, 并说你的重载和
sign.cppview raw1 2 3 4
| template <class CharT, class Traits, class Allocator> std::basic_ostream<CharT, Traits> & operator<<(std::basic_ostream<CharT, Traits> &, const std::basic_string<CharT, Traits, Allocator> &);
|
撞车了
为什么会这样呢? 答案在于部分模板特化的 匹配规则,
简单来说, 编译器不能确定你的重载和 <string>
里的重载哪个更特殊, 画成 Hasse 图是这样的:

注意到你的 重载 和
<string> 里的 重载 是不可比的,
所以在匹配时无法决定哪个优先级更高
因此正确的写法应该是这样:
overload2.cppview raw1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #include <iostream> #include <set> #include <vector>
template <class CharT, class Traits, class Container, decltype(std::declval<Container>().begin(), std::declval<Container>().end(), 0) * = nullptr> std::basic_ostream<CharT, Traits> & operator<<(std::basic_ostream<CharT, Traits> &os, const Container &container) { if (container.begin() == container.end()) return os << "[]"; os << '['; for (auto it = container.begin(); it != container.end();) { std::cout << *it++; if (it != container.end()) std::cout << ", "; } os << ']'; return os; }
int main() { std::vector<int> v{1, 1, 4, 5, 1, 4}; std::cout << v << std::endl; std::set<int> s{1, 1, 4, 5, 1, 4}; std::cout << s << std::endl; std::string str = "114514"; std::cout << str << std::endl; return 0; }
|
参考资料