#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));
}
main函数的最后一行是给我带来麻烦的原因。使用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 ++给出了一个看起来很奇怪的错误消息。但是,这对我来说并没有太大意义,我找不到相关的标准条款。
注意:我知道向上声明是clang要求的,但是我不明白为什么。我想提供print_arg
一个单独的头功能,并允许包括头部专门的人operator<<
在使用print_arg
。
C ++标准指出,可以通过两种方式查找不合格的名称。根据Clang关于语言兼容性的文档:
首先,编译器会在写入名称的范围内进行不限定条件的查找。对于模板,这意味着在定义模板的位置而不是在实例化的位置进行查找。
其次,如果名称像函数一样被调用,则编译器还将执行依赖于参数的查找(ADL)。有时,不合格的查询可以抑制ADL。在ADL中,编译器查看调用的所有参数的类型。当找到一个类类型时,它会在该类的名称空间中查找名称。结果是它在那些命名空间中找到的所有声明,以及来自非限定查找的声明。但是,直到知道所有参数类型,编译器才执行ADL。
有两种方法可以解决此问题:
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句