惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Martin Fowler
Martin Fowler
J
Java Code Geeks
博客园 - 【当耐特】
宝玉的分享
宝玉的分享
腾讯CDC
D
DataBreaches.Net
Microsoft Azure Blog
Microsoft Azure Blog
Engineering at Meta
Engineering at Meta
V
V2EX
F
Fortinet All Blogs
MyScale Blog
MyScale Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
T
Tailwind CSS Blog
Jina AI
Jina AI
GbyAI
GbyAI
大猫的无限游戏
大猫的无限游戏
A
About on SuperTechFans
酷 壳 – CoolShell
酷 壳 – CoolShell
爱范儿
爱范儿
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
U
Unit 42
B
Blog
M
MIT News - Artificial intelligence
N
Netflix TechBlog - Medium

Tifa's Blog

随笔 - Miller-Rabin + Pollard-Rho 分解质因子的时间复杂度分析 随笔 - 批量重命名 APK 文件的 Python 脚本 VP 记录 - 2021 CCPC 哈尔滨站 VP 记录 - 2023 ICPC 亚洲区域赛 (南京) VP 记录 - 2023 CCPC 哈尔滨站 VP 记录 - 2023 CCPC 桂林站 VP 记录 - 2023 ICPC 亚洲区域赛 (网络预选赛 Ⅰ) VP 记录 - 2021 ICPC 亚洲区域赛 (澳门) 题解 - [Luogu P7486] 「Stoi2031」彩虹 拟阵简介(unfin) 题解 - [Luogu P5824] 十二重计数法 随笔 - C++ 的高维向量 | Tifa's Blog 目录 - 算法竞赛模板 | Tifa's Blog 目录 - 学术垃圾 | Tifa's Blog 比赛记录 - Codeforces Round #842 (Div. 2) 比赛记录 - Hello 2023 | Tifa's Blog 比赛记录 - Codeforces Round #841 (Div. 2) and Divide by Zero 2022 随笔 - C++ 基于标签分发的线性筛 | Tifa's Blog VP 记录 - 2022 ICPC 亚洲区域赛 (杭州) VP 记录 - 2022 ICPC 亚洲区域赛 (济南)
随笔 - 关于 C++ 模板的部分特化
Tifa · 2025-02-26 · via Tifa's Blog

有一天你想对容器重载运算符, 但是却出现了神秘问题

在日复一日地对容器书写

for.cppview raw
1
2
3
4
for (auto it = container.begin(); it != container.end();) {
std::cout << *it++;
if (it != container.end()) std::cout << ", ";
}

后, 你终于受不了了, 于是你决定写一个重载来一劳永逸地解决这个问题

overload.cppview raw
1
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 raw
1
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 raw
1
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;
}

参考资料