Clang 6, clang 7, and gcc 7.1, 7.2, and 7.3 all agree that the following is valid C++17 code, but is ambiguous under C++14 and C++11. MSVC 2015 and 2017 accept it as well. However, gcc-8.1 and 8.2 reject it even in c++17 mode:
struct Foo
{
explicit Foo(int ptr);
};
template<class T>
struct Bar
{
operator T() const;
template<typename T2>
explicit operator T2() const;
};
Foo foo(Bar<char> x)
{
return (Foo)x;
}
The compilers that accept it pick the templated explicit conversion function, Bar::operator T2().
The compilers that reject it agree that there is an ambiguity between:
- the explicit conversion function Bar::operator int()
- first using the implicit user-defined conversion from
Bar<char>tochar, then the implicit built-in conversion fromchartoint, and then the explicit constructor Foo(int).
So, which compiler is right? What is the relevant difference in the standard between C++14 and C++17?
Appendix: actual error messages
Here's the error for gcc-8.2 -std=c++17. gcc-7.2 -std=c++14 prints the same error:
<source>: In function 'Foo foo(Bar<char>)':
<source>:17:17: error: call of overloaded 'Foo(Bar<char>&)' is ambiguous
return (Foo)x;
^
<source>:3:14: note: candidate: 'Foo::Foo(int)'
explicit Foo(int ptr);
^~~
<source>:1:8: note: candidate: 'constexpr Foo::Foo(const Foo&)'
struct Foo
^~~
<source>:1:8: note: candidate: 'constexpr Foo::Foo(Foo&&)'
And here's the error from clang-7 -std=c++14 (clang-7 -std=c++17 accepts the code):
<source>:17:12: error: ambiguous conversion for C-style cast from 'Bar<char>' to 'Foo'
return (Foo)x;
^~~~~~
<source>:1:8: note: candidate constructor (the implicit move constructor)
struct Foo
^
<source>:1:8: note: candidate constructor (the implicit copy constructor)
<source>:3:14: note: candidate constructor
explicit Foo(int ptr);
^
1 error generated.