Operador ambiguo en gcc

13

Hice una plantilla de función para imprimir algunos de los contenedores 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;
}

Esto compila y funciona como se esperaba en MSVC, Clang e ICC, pero al compilar con GCC (troncal) da un operator<<error ambiguo para la línea os << elem << " ". E incluso este error aparece solo cuando se compila con la bandera -std=c++17o -std=c++2a.

El error parece razonable, std::stringya que el compilador detecta una plantilla de función existente que para global operator<<que acepta una secuencia de salida y a basic_string<CharT, Traits, Allocator>, con el Allocatortipo predeterminado std::allocator.

Mi pregunta sería por qué compila y funciona con los otros 3 compiladores, según tengo entendido, Clang al menos usa la misma implementación de biblioteca estándar en Linux que gcc, por lo que tiene la misma plantilla de función para operator<<

El error reportado es

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

Y los dos candidatos

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>]'

Argumentos del compilador para GCC, Clang e ICC

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

Un para MSVC

/std:c++latest /O2 /W3

Enlace obligatorio de Godbolt: https://godbolt.org/z/R_aSKR

Petok Lorand
fuente

Respuestas:

8

El error parece razonable, std::stringya que el compilador detecta una plantilla de función existente que para global operator<<que acepta una secuencia de salida y a basic_string<CharT, Traits, Allocator>, con el Allocatortipo predeterminado std::allocator.

Esta capacidad de hacer coincidir un parámetro similar C<T, A>a un tipo similar basic_string<CharT, Traits, Allocator=std::allocator<CharT>>es nueva en C ++ 17, proviene de P0522 . Antes de ese documento, su operador no sería considerado como candidato.

Sin embargo, clang elige intencionalmente no implementar esta función de manera predeterminada. De su estado :

A pesar de ser la resolución de un Informe de defectos, esta función está deshabilitada de manera predeterminada en todas las versiones de idioma, y ​​puede habilitarse explícitamente con la -frelaxed-template-template-argsmarca en Clang 4 en adelante. El cambio al estándar carece de un cambio correspondiente para el pedido parcial de la plantilla, lo que resulta en errores de ambigüedad para un código razonable y previamente válido. Se espera que este problema se solucione pronto.

Puede ver que cuando agrega esa bandera, su código también se vuelve ambiguo en el sonido metálico. Su ejemplo es el tipo de código razonable y previamente válido contra el que clang está protegiendo aquí. Un tipo similar de ejemplo que he visto:

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>> solía estar bien (usando la versión binaria), pero ahora se vuelve ambiguo (entre la versión unaria y la binaria).

MSVC podría hacer la misma elección, pero no lo sé. La respuesta correcta según el estándar es que la llamada es ambigua.

Barry
fuente