Mehrdeutiger Operator in gcc

13

Ich habe eine Funktionsvorlage zum Drucken einiger STL-Container erstellt

#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;
}

Dies kompiliert und funktioniert wie erwartet unter MSVC, Clang und ICC, aber beim Kompilieren mit GCC (Trunk) wird ein mehrdeutiger operator<<Fehler für die Leitung ausgegeben os << elem << " ". Und selbst dieser Fehler tritt nur beim Kompilieren mit dem Flag -std=c++17oder auf -std=c++2a.

Der Fehler erscheint vernünftig, std::stringda der Compiler eine vorhandene Funktionsvorlage erkennt, die für global operator<<einen Ausgabestream und a akzeptiert basic_string<CharT, Traits, Allocator>, wobei der AllocatorTyp standardmäßig verwendet wird std::allocator.

Meine Frage wäre, warum es kompiliert und mit den anderen 3 Compilern zusammenarbeitet. Nach meinem Verständnis verwendet Clang unter Linux dieselbe Standardbibliotheksimplementierung wie gcc, sodass es dieselbe Funktionsvorlage für das hat operator<<

Der gemeldete Fehler ist

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

Und die beiden Kandidaten

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

Compiler-Argumente für GCC, Clang und ICC

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

Ein für MSVC

/std:c++latest /O2 /W3

Obligatorischer Godbolt-Link: https://godbolt.org/z/R_aSKR

Petok Lorand
quelle

Antworten:

8

Der Fehler erscheint vernünftig, std::stringda der Compiler eine vorhandene Funktionsvorlage erkennt, die für global operator<<einen Ausgabestream und a akzeptiert basic_string<CharT, Traits, Allocator>, wobei der AllocatorTyp standardmäßig verwendet wird std::allocator.

Diese Fähigkeit, einen Parameter wie C<T, A>mit einem Typ wie abzugleichen,basic_string<CharT, Traits, Allocator=std::allocator<CharT>> ist neu in C ++ 17 und stammt von P0522 . Vor diesem Papier wurde Ihr Operator nicht als Kandidat angesehen.

Clang entscheidet sich jedoch absichtlich dafür, diese Funktion nicht standardmäßig zu implementieren. Aus ihrem Status :

Obwohl es sich um die Lösung für einen Fehlerbericht handelt, ist diese Funktion in allen Sprachversionen standardmäßig deaktiviert und kann -frelaxed-template-template-argsab Clang 4 explizit mit dem Flag aktiviert werden. Der Änderung des Standards fehlt eine entsprechende Änderung für die Teilbestellung der Vorlage, was zu Mehrdeutigkeitsfehlern für angemessenen und zuvor gültigen Code führt. Dieses Problem wird voraussichtlich bald behoben.

Sie können sehen, dass Ihr Code beim Hinzufügen dieses Flags auch beim Klirren mehrdeutig wird. Ihr Beispiel ist die Art von vernünftigem und zuvor gültigem Code, vor dem Clang hier schützt. Ein ähnliches Beispiel habe ich gesehen:

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>> Früher war es in Ordnung (mit der Binärversion), aber jetzt wird es mehrdeutig (zwischen der unären und der Binärversion).

MSVC könnte die gleiche Wahl treffen, aber ich weiß es nicht. Die richtige Antwort laut Standard ist, dass der Anruf nicht eindeutig ist.

Barry
quelle