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
The error seems reasonable, for
std::string
, since compiler detects an existing function template that for globaloperator<<
that accepts an output stream and abasic_string<CharT, Traits, Allocator>
, with theAllocator
type being defaulted tostd::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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With