clang and gcc differ in behaviour for the following code:
struct foo
{
foo(int);
};
struct waldo
{
template <typename T>
operator T();
};
int main()
{
waldo w;
foo f{w};
}
This code is accepted by clang, with the foo(int)
constructor being called. However, gcc complains about an ambiguity between the foo(int)
constructor and the implicitly generated copy and move constructors:
test.cpp: In function 'int main()':
test.cpp:15:12: error: call of overloaded 'foo(<brace-enclosed initializer list>)' is ambiguous
foo f{w};
^
test.cpp:15:12: note: candidates are:
test.cpp:3:5: note: foo::foo(int)
foo(int);
^
test.cpp:1:8: note: constexpr foo::foo(const foo&)
struct foo
^
test.cpp:1:8: note: constexpr foo::foo(foo&&)
Who is right?
It's also interesting to note that if foo f{w}
is changed to foo f(w)
(note the change from braces to parentheses), both gcc and clang give an error. This makes me hope that gcc's behaviour for the above example (i.e. giving an error) is correct, otherwise there would be a strange inconsistency between the ()
and {}
forms of initialization.
EDIT: Following Kerrek SB's suggestion, I tried delete
ing the copy constructor of foo
:
struct foo
{
foo(int);
foo(const foo&) = delete;
};
The behaviour remains the same.
For list initialization, if the element of the list has one element (here, w
), and a constructor of a class X
with parameter "reference to const/volatile X" is considered, no user defined conversions are considered. So both the copy and move constructor of foo
cannot be used. So the foo(int)
constructor is unambiguously chosen.
So Clang is correct here.
EDIT: For the Standards folks in here, see 13.3.3.1p4
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