Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ambigous operator in gcc

Tags:

c++

c++17

I made a function template for printing some of the stl containers

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

This compiles and works as expected on MSVC, Clang and ICC, but when compiling with GCC(trunk) it gives an ambigous operator<< error for the line os << elem << " ". And even this error appears only when compiling with the flag -std=c++17 or -std=c++2a.

The error seems reasonable, for std::string, since compiler detects an existing function template that for global operator<< that accepts an output stream and a basic_string<CharT, Traits, Allocator>, with the Allocator type being defaulted to std::allocator.

My question would be why does it compile and work with the other 3 compilers, from my understanding ,Clang at least, uses the same standard library implementation on linux as gcc, so it has the same function template for the operator<<

The error reported is

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

And the two candidates

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 arguments for GCC, Clang and ICC

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

An for MSVC

/std:c++latest /O2 /W3

Mandatory godbolt link: https://godbolt.org/z/R_aSKR

like image 425
Petok Lorand Avatar asked Oct 03 '19 11:10

Petok Lorand


1 Answers

The error seems reasonable, for std::string, since compiler detects an existing function template that for global operator<< that accepts an output stream and a basic_string<CharT, Traits, Allocator>, with the Allocator type being defaulted to std::allocator.

This ability to match a parameter like C<T, A> to a type like basic_string<CharT, Traits, Allocator=std::allocator<CharT>> is new in C++17, it comes from P0522. Prior to that paper, your operator would not be considered as a candidate.

However, clang intentionally chooses not to implement this feature by default. From their status:

Despite being the resolution to a Defect Report, this feature is disabled by default in all language versions, and can be enabled explicitly with the flag -frelaxed-template-template-args in Clang 4 onwards. The change to the standard lacks a corresponding change for template partial ordering, resulting in ambiguity errors for reasonable and previously-valid code. This issue is expected to be rectified soon.

You can see that when you add that flag, your code becomes ambiguous on clang as well. Your example is the kind of reasonable and previously-valid code that clang is protecting against here. A similar kind of example I've seen:

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>> used to be okay (using the binary version), but now becomes ambiguous (between the unary and the binary version).

MSVC might make the same choice, but I do not know. The correct answer per the standard is that the call is ambiguous.

like image 139
Barry Avatar answered Oct 19 '22 01:10

Barry