Operador ambíguo em gcc

13

Eu criei um modelo de função para imprimir alguns contêineres 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;
}

Isso compila e funciona como esperado no MSVC, Clang e ICC, mas ao compilar com o GCC (tronco), gera um operator<<erro ambíguo para a linha os << elem << " ". E mesmo esse erro aparece apenas ao compilar com a bandeira -std=c++17ou -std=c++2a.

O erro parece razoável, pois std::string, já que o compilador detecta um modelo de função existente que, para global, operator<<aceita um fluxo de saída e um basic_string<CharT, Traits, Allocator>, com o Allocatortipo padrão std::allocator.

Minha pergunta seria por que ele compila e trabalha com os outros 3 compiladores, pelo que entendi, pelo menos Clang, usa a mesma implementação de biblioteca padrão no linux que o gcc, para que ele tenha o mesmo modelo de função para o operator<<

O erro relatado é

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

E os dois 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 do compilador para GCC, Clang e ICC

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

Um para MSVC

/std:c++latest /O2 /W3

Link obrigatório godbolt: https://godbolt.org/z/R_aSKR

Petok Lorand
fonte

Respostas:

8

O erro parece razoável, pois std::string, já que o compilador detecta um modelo de função existente que, para global, operator<<aceita um fluxo de saída e um basic_string<CharT, Traits, Allocator>, com o Allocatortipo padrão std::allocator.

Essa capacidade de corresponder um parâmetro como C<T, A>um tipo basic_string<CharT, Traits, Allocator=std::allocator<CharT>>é novo no C ++ 17, vem do P0522 . Antes desse documento, seu operador não seria considerado candidato.

No entanto, o clang escolhe intencionalmente não implementar esse recurso por padrão. De seu status :

Apesar de ser a resolução de um Relatório de Defeitos, esse recurso é desativado por padrão em todas as versões de idioma e pode ser ativado explicitamente com o sinalizador -frelaxed-template-template-argsno Clang 4 em diante. A alteração no padrão carece de uma alteração correspondente na ordem parcial do modelo, resultando em erros de ambiguidade para código razoável e válido anteriormente. Espera-se que esse problema seja corrigido em breve.

Você pode ver que quando você adiciona esse sinalizador, seu código também se torna ambíguo no clang. Seu exemplo é o tipo de código razoável e válido anteriormente que o clang está protegendo aqui. Um tipo semelhante de exemplo que eu já vi:

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>> costumava ficar bem (usando a versão binária), mas agora se torna ambígua (entre a versão unária e a binária).

MSVC pode fazer a mesma escolha, mas eu não sei. A resposta correta de acordo com o padrão é que a chamada é ambígua.

Barry
fonte