Неоднозначный оператор в gcc

13

Я сделал шаблон функции для печати некоторых контейнеров 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;
}

Это компилируется и работает, как ожидается, на MSVC, Clang и ICC, но при компиляции с GCC (транк) это дает неоднозначную operator<<ошибку для линии os << elem << " ". И даже эта ошибка появляется только при компиляции с флагом -std=c++17или -std=c++2a.

Ошибка кажется разумной, std::stringпоскольку компилятор обнаруживает существующий шаблон функции, который для глобальной operator<<переменной принимает выходной поток и a basic_string<CharT, Traits, Allocator>с Allocatorтипом по умолчанию std::allocator.

Мой вопрос заключается в том, почему он компилируется и работает с другими 3 компиляторами, насколько я понимаю, Clang, по крайней мере, использует ту же стандартную библиотечную реализацию в linux, что и gcc, поэтому он имеет тот же шаблон функции для operator<<

Сообщение об ошибке

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

И два кандидата

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

Аргументы компилятора для GCC, Clang и ICC

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

Для MSVC

/std:c++latest /O2 /W3

Обязательная ссылка на Godbolt: https://godbolt.org/z/R_aSKR

Петок Лоран
источник

Ответы:

8

Ошибка кажется разумной, std::stringпоскольку компилятор обнаруживает существующий шаблон функции, который для глобальной operator<<переменной принимает выходной поток и a basic_string<CharT, Traits, Allocator>с Allocatorтипом по умолчанию std::allocator.

Эта способность сопоставлять параметр, подобный C<T, A>типу, basic_string<CharT, Traits, Allocator=std::allocator<CharT>>является новой в C ++ 17, она исходит от P0522 . До этого документа ваш оператор не считался кандидатом.

Тем не менее, Clang намеренно решает не реализовывать эту функцию по умолчанию. Из их статуса :

Несмотря на то, что это разрешение для отчета о дефектах, эта функция по умолчанию отключена во всех языковых версиях и может быть включена явно с флагом -frelaxed-template-template-argsв Clang 4 и выше. Изменению в стандарте не хватает соответствующего изменения для частичного упорядочения шаблона, что приводит к ошибкам неоднозначности для разумного и ранее действительного кода. Эта проблема, как ожидается, будет исправлена ​​в ближайшее время.

Вы можете видеть, что когда вы добавляете этот флаг, ваш код становится неоднозначным и в Clang. Ваш пример - это разумный и ранее действующий код, от которого защищает Clang. Пример подобного рода, который я видел:

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>> Раньше все было в порядке (используется бинарная версия), но теперь становится неоднозначным (между унарной и бинарной версией).

MSVC может сделать такой же выбор, но я не знаю. Правильный ответ по стандарту заключается в том, что вызов неоднозначен.

Барри
источник