Ambigious operator w gcc

13

Stworzyłem szablon funkcji do drukowania niektórych pojemników 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;
}

Kompiluje się i działa zgodnie z oczekiwaniami w MSVC, Clang i ICC, ale podczas kompilacji z GCC (trunk) daje niejednoznaczny operator<<błąd linii os << elem << " ". I nawet ten błąd pojawia się tylko podczas kompilacji z flagą -std=c++17lub -std=c++2a.

Błąd wydaje się rozsądny, std::stringponieważ, ponieważ kompilator wykrywa istniejący szablon funkcji, który dla globalnego, operator<<który akceptuje strumień wyjściowy i a basic_string<CharT, Traits, Allocator>, przy Allocatordomyślnym typie std::allocator.

Moje pytanie brzmi: dlaczego kompiluje się i współpracuje z pozostałymi 3 kompilatorami, z mojego zrozumienia, przynajmniej Clang, używa tej samej standardowej implementacji biblioteki w systemie Linux jak gcc, więc ma ten sam szablon funkcji dla operator<<

Zgłoszony błąd to

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

I dwóch kandydatów

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

Argumenty kompilatora dla GCC, Clang i ICC

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

An dla MSVC

/std:c++latest /O2 /W3

Obowiązkowy godbolt link: https://godbolt.org/z/R_aSKR

Petok Lorand
źródło

Odpowiedzi:

8

Błąd wydaje się rozsądny, std::stringponieważ, ponieważ kompilator wykrywa istniejący szablon funkcji, który dla globalnego, operator<<który akceptuje strumień wyjściowy i a basic_string<CharT, Traits, Allocator>, przy Allocatordomyślnym typie std::allocator.

Ta możliwość dopasowania parametru typu C<T, A>do typu basic_string<CharT, Traits, Allocator=std::allocator<CharT>>nowego jest nowa w C ++ 17 i pochodzi z P0522 . Przed tym opracowaniem operator nie byłby uznawany za kandydata.

Jednak clang celowo postanawia nie implementować tej funkcji domyślnie. Z ich statusu :

Mimo że jest to rozdzielczość raportu o defektach, ta funkcja jest domyślnie wyłączona we wszystkich wersjach językowych i można ją wyraźnie włączyć za pomocą flagi -frelaxed-template-template-argsw Clang 4 i nowszych. W zmianie standardu brakuje odpowiedniej zmiany w częściowym porządkowaniu szablonów, co powoduje błędy niejednoznaczności dla rozsądnego i wcześniej poprawnego kodu. Ten problem wkrótce zostanie naprawiony.

Widać, że po dodaniu tej flagi kod staje się niejednoznaczny również w przypadku clang. Twój przykład jest rodzajem rozsądnego i wcześniej ważnego kodu, przed którym chroni clang. Podobny przykład widziałem:

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>> kiedyś było w porządku (przy użyciu wersji binarnej), ale teraz staje się niejednoznaczne (między wersją jednoargumentową a wersją binarną).

MSVC może dokonać tego samego wyboru, ale nie wiem. Prawidłowa odpowiedź zgodnie ze standardem polega na tym, że połączenie jest niejednoznaczne.

Barry
źródło