gcc中的模糊运算符

Pet*_*and 13 c++ c++17

我制作了一个用于打印一些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;
}
Run Code Online (Sandbox Code Playgroud)

这可以在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>')
Run Code Online (Sandbox Code Playgroud)

和两位候选人

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>]'
Run Code Online (Sandbox Code Playgroud)

GCC,Clang和ICC的编译器参数

-std=c++2a -O3 -Wall -Wextra -Wpedantic -Werror
Run Code Online (Sandbox Code Playgroud)

对于MSVC

/std:c++latest /O2 /W3
Run Code Online (Sandbox Code Playgroud)

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

Bar*_*rry 8

The error seems reasonable, for std::string, since compiler detects an existing function template that for global operator<< that accepts an output stream and a basic_string<CharT, Traits, Allocator>, with the Allocator type being defaulted to std::allocator.

This ability to match a parameter like C<T, A> to a type like basic_string<CharT, Traits, Allocator=std::allocator<CharT>> is new in C++17, it comes from P0522. Prior to that paper, your operator would not be considered as a candidate.

However, clang intentionally chooses not to implement this feature by default. From their status:

Despite being the resolution to a Defect Report, this feature is disabled by default in all language versions, and can be enabled explicitly with the flag -frelaxed-template-template-args in Clang 4 onwards. The change to the standard lacks a corresponding change for template partial ordering, resulting in ambiguity errors for reasonable and previously-valid code. This issue is expected to be rectified soon.

You can see that when you add that flag, your code becomes ambiguous on clang as well. Your example is the kind of reasonable and previously-valid code that clang is protecting against here. A similar kind of example I've seen:

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>> { /* ... */ };
Run Code Online (Sandbox Code Playgroud)

some_trait<vector<int>> used to be okay (using the binary version), but now becomes ambiguous (between the unary and the binary version).

MSVC可能会做出相同的选择,但我不知道。每个标准的正确答案是呼叫不明确。