gcc 中的模棱两可的运算符



>我做了一个函数模板来打印一些stl容器

#include <iostream>
#include <vector>
#include <string>
template <template <typename, typename> class C, typename T, typename A>
std::ostream& operator<<(std::ostream& os, const C<T, A>& container)
{ 
for (auto& elem : container) 
{ 
os << elem << " "; 
} 
return os; 
}
int main()
{
std::vector<std::string> v { "One", "Two", "Three" };
std::cout << v << std::endl;
return 0;
}

这在MSVC,Clang和ICC上按预期编译和工作,但是当使用GCC(trunk(编译时,它会为行os << elem << " "给出一个不明确的operator<<错误。甚至此错误也仅在使用标志进行编译时才出现-std=c++17-std=c++2a.

对于std::string来说,该错误似乎是合理的,因为编译器检测到一个现有的函数模板,对于接受输出流和basic_string<CharT, Traits, Allocator>的全局operator<<Allocator类型默认为std::allocator

我的问题是为什么它编译并与其他 3 个编译器一起工作,据我了解,Clang 至少在 linux 上使用与 gcc 相同的标准库实现,因此它具有相同的函数模板operator<<

报告的错误是

error: ambiguous overload for 'operator<<' (operand types are 'std::ostream' {aka 'std::basic_ostream<char>'} and 'const std::__cxx11::basic_string<char>')

而两位候选人

note: candidate: 'std::ostream& operator<<(std::ostream&, const C<T, A>&) [with C = std::__cxx11::basic_string; T = char; A = std::char_traits<char>; std::ostream = std::basic_ostream<char>]'
note: candidate: 'std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&, const std::__cxx11::basic_string<_CharT, _Traits, _Allocator>&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]'

GCC、Clang 和 ICC 的编译器参数

-std=c++2a -O3 -Wall -Wextra -Wpedantic -Werror

一个代表 MSVC

/std:c++latest /O2 /W3

强制锁链:https://godbolt.org/z/R_aSKR

对于std::string来说,这个错误似乎是合理的,因为编译器检测到一个现有的函数模板,对于接受输出流和basic_string<CharT, Traits, Allocator>的全局operator<<Allocator类型默认为std::allocator

这种将C<T, A>等参数与basic_string<CharT, Traits, Allocator=std::allocator<CharT>>等类型匹配的功能在 C++17 中是新的,它来自 P0522。在该论文之前,您的操作员不会被视为候选人。

但是,clang 有意选择默认不实现此功能。从他们的状态来看:

尽管是缺陷报告的解决方案,但此功能在所有语言版本中默认处于禁用状态,并且可以使用 Clang 4 及更高版本中的标志-frelaxed-template-template-args显式启用此功能。对标准的更改缺少对模板部分排序的相应更改,导致合理且先前有效的代码出现歧义错误。预计这个问题将很快得到纠正。

您可以看到,当您添加该标志时,您的代码在 clang 上也会变得不明确。您的示例是 clang 在此处保护的那种合理且以前有效的代码。我见过的一个类似的例子:

template <class T> struct some_trait;
template <template <class> class C, class A>
struct some_trait<C<A>> { /* ... */ };
template <template <class> class C, class A, class B>
struct some_trait<C<A, B>> { /* ... */ };

some_trait<vector<int>>过去还可以(使用二进制版本(,但现在变得模棱两可(在一元和二进制版本之间(。

MSVC可能会做出同样的选择,但我不知道。根据标准的正确答案是呼叫模棱两可。

最新更新