Reputation: 210455
I don't understand why the code below prints struct Value
instead of int
(which implies the conversion constructor is converting to Value
instead of int
). (Visual C++ 2012)
Why is this happening? Why does the compiler completely ignore the Value(int)
constructor?
#include <iostream>
#include <type_info>
using namespace std;
struct Value { Value(int) { } };
struct Convertible
{
template<class T>
operator T() const
{ throw typeid(T).name(); }
};
int main()
{
try { Value w((Convertible())); }
catch (char const *s) { cerr << s << endl; }
}
Even more bizarre is this (this time it's C++11 only, on GCC 4.7.2):
#include <iostream>
#include <typeinfo>
using namespace std;
struct Value
{
Value(Value const &) = delete;
Value(int) { }
};
struct Convertible
{
template<class T>
operator T() const
{ throw typeid(T).name(); }
};
int main()
{
try { Value w((Convertible())); }
catch (char const *s) { cerr << s << endl; }
}
Which gives:
source.cpp: In function 'int main()':
source.cpp:21:32: error: call of overloaded 'Value(Convertible)' is ambiguous
source.cpp:21:32: note: candidates are:
source.cpp:9:3: note: Value::Value(int)
source.cpp:8:3: note: Value::Value(const Value&) <deleted>
If the copy constructor is deleted, then why is there any ambiguity?!
Upvotes: 11
Views: 825
Reputation: 157354
In the first example Visual Studio is incorrect; the call is ambiguous. gcc in C++03 mode prints:
source.cpp:21:34: error: call of overloaded 'Value(Convertible)' is ambiguous
source.cpp:21:34: note: candidates are:
source.cpp:9:5: note: Value::Value(int)
source.cpp:6:8: note: Value::Value(const Value&)
Recall that a copy constructor is implicitly defaulted. The governing paragraph is 13.3.1.3 Initialization by constructor [over.match.ctor]:
When objects of class type are direct-initialized [...], overload resolution selects the constructor. For direct-initialization, the candidate functions are all the constructors of the class of the object being initialized.
In the second example, deleted functions participate equally in overload resolution; they only affect compilation once overloads have been resolved, when a program that selects a deleted function is ill-formed. The motivating example in the standard is of a class that can only be constructed from floating-point types:
struct onlydouble {
onlydouble(std::intmax_t) = delete;
onlydouble(double);
};
Upvotes: 8
Reputation: 1823
I've tried your code (on the Visual Studio version only).
Since you have a built-in copy-CTOR, I guess your main is equal to:
int main()
{
try { Value w((struct Value)(Convertible())); }
catch (char const *s) { cerr << s << endl; }
}
The compiler has chosen to use your copy CTOR, rather than Value(int).
Changing it to:
int main()
{
try { Value w((int)(Convertible())); }
catch (char const *s) { cerr << s << endl; }
}
It printed "int".
Upvotes: 1