我有以下代码示例(可在coliru上在线获得:]
#include <iostream>
#include <utility>
struct Bar {
int a;
};
template <class T>
void print_arg(const T& arg) {
std::cout << arg << std::endl;
}
std::ostream& operator<<(std::ostream& os, const Bar& b) {
os << b.a;
return os;
}
template <class T1, class T2>
std::ostream& operator<<(std::ostream& os, const std::pair<T1, T2>& pair) {
os << "Pair(" << pair.first << ',' << pair.second << ")";
return os;
}
int main()
{
auto bar = Bar{1};
print_arg(bar);
print_arg(std::make_pair(bar, bar));
print_arg(std::make_pair(bar, 1));
print_arg(std::make_pair(0, 1));
}
主要功能的最后一行给我带来了麻烦。使用g ++进行编译可以很好地工作(具有与下面完全相同的选项),我启动可执行文件并按预期打印所有内容。但是,Clang ++给了我以下错误:
$ clang++ -std=c++17 -O2 -Wall -Werror -Wpedantic main.cpp && ./a.out
main.cpp:10:15: error: call to function 'operator<<' that is neither visible in the template definition nor found by argument-dependent lookup
std::cout << arg << std::endl;
^
main.cpp:29:5: note: in instantiation of function template specialization 'print_arg<std::pair<int, int> >' requested here
print_arg(std::make_pair(0, 1));
^
main.cpp:19:15: note: 'operator<<' should be declared prior to the call site
std::ostream& operator<<(std::ostream& os, const std::pair<T, T>& pair) {
^
1 error generated.
此外,删除最后一行(将其注释掉)会导致Clang ++正确编译所有内容。据我所知,这意味着std::pair<int, int>
在质量上与其他参数类型不同。
我的问题是,为什么g ++仍要编译它?更重要的是,为什么clang认为以后再声明operator<<(ostream, pair<Bar, Bar>)
是可以的,但是对于operator<<(ostream, pair<int, int>)
却不是可以的。是否因为后者仅包括标准类型和基本类型?
对我来说(某种程度上)的逻辑似乎是仅在标准/基本类型上定义函数是UB,但是g ++默默地忽略了它,而clang ++给出了一个看起来很奇怪的错误消息。但是,这对我来说没有太大意义,我找不到相关的标准条款。
Note:我知道向上声明是clang所要求的,但是我不明白为什么。我想在单独的标题中提供print_arg
函数,并允许使用该标题的人在使用operator<<
时专门使用print_arg
。
查看Language Compatibility : Unqualified lookup in templates部分。它确切地解释了这种情况。
摘要为GCC
编译错误代码,而clang遵循标准。