gcc中的模糊运算符


13

我制作了一个用于打印一些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)进行编译时,会给operator<<该行带来大量错误os << elem << " "。并且甚至仅当使用标志-std=c++17或进行编译时,才会出现此错误-std=c++2a

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

我的问题是,为什么从我的理解(至少是Clang)开始,它为什么可以与其他3个编译器一起编译并与之一起工作,所以在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

强制性Godbolt链接:https ://godbolt.org/z/R_aSKR

Answers:


8

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

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

但是,clang故意选择默认情况下不实现此功能。从他们的状态

尽管可以解决缺陷报告,但默认情况下,所有语言版本均禁用此功能,并且可以使用-frelaxed-template-template-argsClang 4 及更高版本中的标记显式启用此功能。对标准的更改缺少模板部分排序的相应更改,从而导致合理且先前有效的代码存在歧义错误。预计此问题将很快得到纠正。

您可以看到,添加该标志时,您的代码在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可能会做出相同的选择,但我不知道。每个标准的正确答案是呼叫不明确。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.