Operator ambigous dalam gcc

13

Saya membuat templat fungsi untuk mencetak beberapa wadah 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;
}

Ini mengkompilasi dan bekerja seperti yang diharapkan pada MSVC, Dentang dan ICC, tetapi ketika dikompilasi dengan GCC (trunk) itu memberikan operator<<kesalahan ambigu untuk saluran os << elem << " ". Dan bahkan kesalahan ini hanya muncul ketika kompilasi dengan bendera -std=c++17atau -std=c++2a.

Kesalahan tersebut tampaknya masuk akal, karena std::string, compiler mendeteksi templat fungsi yang sudah ada yang untuk global operator<<yang menerima aliran output dan basic_string<CharT, Traits, Allocator>, dengan Allocatortipe yang default std::allocator.

Pertanyaan saya adalah mengapa ia mengkompilasi dan bekerja dengan 3 kompiler lainnya, dari pemahaman saya, setidaknya Clang, menggunakan implementasi pustaka standar yang sama di linux seperti gcc, sehingga ia memiliki templat fungsi yang sama untuk operator<<

Kesalahan yang dilaporkan adalah

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

Dan dua kandidat

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

Argumen kompiler untuk GCC, Dentang dan ICC

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

Sebuah untuk MSVC

/std:c++latest /O2 /W3

Tautan godbolt wajib: https://godbolt.org/z/R_aSKR

Petok Lorand
sumber

Jawaban:

8

Kesalahan tersebut tampaknya masuk akal, karena std::string, compiler mendeteksi templat fungsi yang sudah ada yang untuk global operator<<yang menerima aliran output dan basic_string<CharT, Traits, Allocator>, dengan Allocatortipe yang default std::allocator.

Kemampuan ini untuk mencocokkan parameter suka C<T, A>ke jenis suka basic_string<CharT, Traits, Allocator=std::allocator<CharT>>baru di C ++ 17, itu berasal dari P0522 . Sebelum makalah itu, operator Anda tidak akan dianggap sebagai kandidat.

Namun, dentang sengaja memilih untuk tidak mengimplementasikan fitur ini secara default. Dari status mereka :

Meskipun resolusi untuk Laporan Cacat, fitur ini dinonaktifkan secara default di semua versi bahasa, dan dapat diaktifkan secara eksplisit dengan bendera -frelaxed-template-template-argsdi Dentang 4 dan seterusnya. Perubahan ke standar tidak memiliki perubahan yang sesuai untuk pemesanan parsial templat, menghasilkan kesalahan ambiguitas untuk kode yang masuk akal dan sebelumnya valid. Masalah ini diharapkan segera diperbaiki.

Anda dapat melihat bahwa ketika Anda menambahkan bendera itu, kode Anda menjadi ambigu pada dentang juga. Contoh Anda adalah jenis kode yang masuk akal dan sebelumnya valid yang dilindungi dentang di sini. Contoh serupa yang pernah saya lihat:

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>> dulu oke (menggunakan versi biner), tapi sekarang menjadi ambigu (antara versi unary dan biner).

MSVC mungkin membuat pilihan yang sama, tapi saya tidak tahu. Jawaban yang benar per standar adalah bahwa panggilan itu ambigu.

Barry
sumber