Opérateur ambigu dans gcc

13

J'ai fait un modèle de fonction pour imprimer certains des conteneurs 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;
}

Cela compile et fonctionne comme prévu sur MSVC, Clang et ICC, mais lors de la compilation avec GCC (tronc), cela donne une operator<<erreur ambiguë pour la ligne os << elem << " ". Et même cette erreur n'apparaît que lors de la compilation avec le drapeau -std=c++17ou -std=c++2a.

L'erreur semble raisonnable, std::stringcar le compilateur détecte un modèle de fonction existant qui, pour global operator<<, accepte un flux de sortie et un basic_string<CharT, Traits, Allocator>, le Allocatortype étant par défaut std::allocator.

Ma question serait de savoir pourquoi il compile et travaille avec les 3 autres compilateurs, à ma connaissance, Clang au moins, utilise la même implémentation de bibliothèque standard sur linux que gcc, donc il a le même modèle de fonction pour le operator<<

L'erreur signalée est

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

Et les deux candidats

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

Arguments du compilateur pour GCC, Clang et ICC

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

Un pour MSVC

/std:c++latest /O2 /W3

Lien obligatoire Godbolt: https://godbolt.org/z/R_aSKR

Petok Lorand
la source

Réponses:

8

L'erreur semble raisonnable, std::stringcar le compilateur détecte un modèle de fonction existant qui, pour global operator<<, accepte un flux de sortie et un basic_string<CharT, Traits, Allocator>, le Allocatortype étant par défaut std::allocator.

Cette possibilité de faire correspondre un paramètre comme C<T, A>à un type comme basic_string<CharT, Traits, Allocator=std::allocator<CharT>>est nouvelle en C ++ 17, elle vient de P0522 . Avant ce document, votre opérateur ne serait pas considéré comme un candidat.

Cependant, clang choisit intentionnellement de ne pas implémenter cette fonctionnalité par défaut. De leur statut :

Bien qu'elle soit la résolution d'un rapport de défaut, cette fonctionnalité est désactivée par défaut dans toutes les versions linguistiques et peut être activée explicitement avec l'indicateur -frelaxed-template-template-argsdans Clang 4. La modification de la norme n'a pas de modification correspondante pour l'ordre partiel du modèle, ce qui entraîne des erreurs d'ambiguïté pour un code raisonnable et précédemment valide. Ce problème devrait être corrigé prochainement.

Vous pouvez voir que lorsque vous ajoutez cet indicateur, votre code devient également ambigu sur clang. Votre exemple est le type de code raisonnable et précédemment valide contre lequel clang se protège ici. Un genre similaire d'exemple que j'ai vu:

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>> utilisé pour être correct (en utilisant la version binaire), mais devient maintenant ambigu (entre la version unaire et la version binaire).

MSVC pourrait faire le même choix, mais je ne sais pas. La bonne réponse selon la norme est que l'appel est ambigu.

Barry
la source