Амбітний оператор в 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 компіляторами, з мого розуміння, Кланг, принаймні, використовує таку ж стандартну реалізацію бібліотеки в 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 і далі. Зміна стандарту не має відповідної зміни для часткового впорядкування шаблону, що призводить до неоднозначності помилок для розумного та раніше дійсного коду. Очікується, що це питання буде вирішено найближчим часом.

Ви можете бачити, що коли ви додаєте цей прапор, ваш код стає неоднозначним і в кланге. Ваш приклад - це такий розумний і раніше дійсний код, від якого тут захищає кланг. Подібний приклад я бачив:

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 може зробити той самий вибір, але я не знаю. Правильна відповідь стандарту полягає в тому, що дзвінок неоднозначний.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.