The Standard says: (N3797::13.3.3.1/4 [over.best.ics])
However, when considering the argument of a constructor or
user-defined conversion function that is a candidate by 13.3.1.3 when
invoked for the copying/moving of the temporary in the second step of
a class copy-initialization, by 13.3.1.7 when passing the initializer
list as a single argument or when the initializer list has exactly one
element and a conversion to some class X or reference to (possibly
cv-qualified) X is considered for the first parameter of a constructor
of X, [...] only standard conversion sequences and ellipsis conversion sequences are considered.
That's we're concerned about the first argument only in the following case:
#include <iostream>
struct B;
struct C
{
C(){ }
C(const B&){ }
};
struct B
{
B(){ }
B(const C&){ }
};
struct A
{
A(const C&, const C&){ }
A(const B&){ }
};
B b;
C c;
A a{b, b};
int main(){ }
DEMO
It works, although I provided b as the first argument and the user-defined conversion B-->C shouldn't be applied.
You have a conversion from a B to an A and a conversion from a C to a B but you are trying to construct an A from {B,C} and there is no constructor of that form. . .
But now you've chanded it completely. :)
Related
From what I understand, in the following code, explicit A(int a) should prevent A b('g'); to use the int constructor:
#include <iostream>
class A
{
public:
int x;
char *y;
A() : x(0) {}
explicit A(int a) : x(a) { std::cout<<"INT\n"; }
A(char *b) : y(b) { std::cout<<"C STRING\n"; }
};
int main()
{
A a(5); /// output: "INT"
A b('g'); /// output: "INT"
A c("Hello"); /// output: "C STRING"
}
However, A b('g'); uses the int constructor... Why?
Also, another question: If I write A(const char *b) instead of A(char *b), it gives me the following error: invalid conversion from 'const char*' to 'char*' [-fpermissive]. Why can't I convert a const char* to char*?
The explicit keyword prevents implicit conversion from int to A. However, it doesn't affect the possibility of char to int conversion when calling A constructor directly.
In other words, explicit only affects conversion to your class A but it doesn't affect any possible conversions of constructor parameters.
You're using direct initialization, which does consider explicit constructors.
Direct-initialization is more permissive than copy-initialization: copy-initialization only considers non-explicit constructors and non-explicit user-defined conversion functions, while direct-initialization considers all constructors and all user-defined conversion functions.
Given A b('g');, 'g' is an char and could convert to int implicitly, then A::A(int) is called to initialize the object.
On the other hand, copy initialization like A a = 5; and A b = 'g'; won't work.
In addition, the implicit conversion in copy-initialization must produce T directly from the initializer, while, e.g. direct-initialization expects an implicit conversion from the initializer to an argument of T's constructor.
And about
Why can't I convert a const char* to char*?
const char* can't convert to char* implicitly. You can use const_cast, but note that it's dangerous, e.g. attempting to modify a string literal results in undefined behavior.
Consider the following program:
struct A {
A(int){}
A(A const &){}
};
int main() {
A y(5);
}
The variable y is direct-initialized with the expression 5.The overload resolution selects the constructor A::A(int), which is what I expect and want, but why does it happen?
It may be so for two reasons:
either the overload A::A(int) is a better match then A::A(A const &), or the second one is not a viable overload at all.
Question: In the above program, is the constructor A::A(A const &) a viable overload for initialization of y?
Yes, the rules for constructor overloading are same as for ordinary functions. The compiler is allowed to make one user-defined conversion per parameter - as pointed by Ben Voigt - in order to match the parameters with arguments. In this case it can do int->A through A(5)
This situation is the same as:
void foo(const std::string&);
void bar(const std::string&);//1
void bar(const char*);//2
//...
foo("Hello");//Is allowed
bar("Hello");//Calls 2 as it matches exactly without a need for conversion.
So, the answer, yes it's viable overload but it's not chosen because according to overloading rules the A(int) constructor is a better match.
[class.conv.ctor]/1:
A constructor that is not explicit ([dcl.fct.spec]) specifies a
conversion from the types of its parameters (if any) to the type of
its class. Such a constructor is called a converting constructor.
[ Example:
struct X {
X(int);
X(const char*, int =0);
X(int, int);
};
void f(X arg)
{
X a = 1; // a = X(1)
X b = "Jessie"; // b = X("Jessie",0)
a = 2; // a = X(2)
f(3); // f(X(3))
f({1, 2}); // f(X(1,2))
}
— end example ]
and
[over.match.copy]/1:
Assuming that “cv1 T” is the type of the object being initialized, with T a class type, the candidate functions are selected as follows:
(1.1) The converting constructors of T are candidate functions.
class Myclass{
private:
int i;
public:
Myclass(){}
Myclass(const Myclass &lvalue){} //<---
template<typename T>Myclass(T& lvalue):i(lvalue){}
};
int main()
{
Myclass a;
Myclass b(a);
return 0;
}
The code above fails to compile with:
error: cannot convert ‘Myclass’ to ‘int’ in initialization
Is this a bug? I have tested it using g++ 5.3 and clang3.9
Nope, not a bug. And this has nothing to do with SFINAE.
Let's do overload resolution on:
Myclass b(a);
We have two viable overloads:
Myclass(Myclass const&); // your copy ctor
Myclass(Myclass& ); // your ctor template, with [T=Myclass]
Both are exact matches. One of the tiebreakers in picking the best viable candidate is to select the least cv-qualified reference - which in this case is the template. This ends up trying to initialize your int with a Myclass, hence the error. (Note, there is a tiebreaker to prefer non-templates to templates - but it's a lower ranked tiebreaker than the cv-qualification on the reference).
In this case, the solution would be to introduce SFINAE to disable this constructor in the case that it should use the copy ctor. That is:
template <class T, class = std::enable_if_t<!std::is_convertible<T*, Myclass const*>::value>>
Myclass(T& );
And now this constructor won't be viable for Myclass b(a).
The other answers are good, but I thought I'd add standard references to complement. The latest draft, section Ranking implicit conversion sequences, states:
Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if
S1 and S2 are reference bindings ([dcl.init.ref]), and the types to which the references refer are the same type except for top-level
cv-qualifiers, and the type to which the reference initialized by S2
refers is more cv-qualified than the type to which the reference
initialized by S1 refers. [ Example:
int f(const int &);
int f(int &);
int g(const int &);
int g(int);
int i;
int j = f(i); // calls f(int &)
The following program prints 42:
#include <iostream>
struct A
{
operator int(){ return 42; }
};
struct B
{
operator A(){ return A(); }
};
B b;
int a = A(b);
int main(){ std::cout << a << std::endl; } //42
DEMO
But if we try to define cope/move or both contructors it won't work.
#include <iostream>
struct A
{
A(A&&){ std::cout << "A(A&&)" << std::endl; }
A(A&){ std::cout << "A(A&)" << std::endl; }
operator int(){ return 42; }
};
struct B
{
operator A(){ return A(); }
};
B b;
int a = A(b);
int main(){ std::cout << a << std::endl; } //Error
DEMO
I thought, the relevant section, describing that behavior is N4296::8.5/17.7 [dcl.init]
If the destination type is a (possibly cv-qualified) class type:
[...]
— Otherwise, if the source type is a (possibly cv-qualified) class type, conversion functions are considered. The applicable conversion
functions are enumerated (13.3.1.5), and the best one is chosen
through overload resolution (13.3). The user-defined conversion so
selected is called to convert the initializer expression into the
object being initialized. If the conversion cannot be done or is
ambiguous, the initialization is ill-formed.
It shouldn't depends on absence/presence of the constructors. We just should have to have appropriate conversion functions so as to choose the conversion sequence.
You effectively deleted the default constructor. From the standard (12.1/4, emphasis mine):
A default constructor for a class X is a constructor of class X that can be called without an argument. If
there is no user-declared constructor for class X, a constructor having no parameters is implicitly declared
as defaulted
IF there is no user-declared constructor. But you declared two, so there is no implicit default constructor. Thus, this:
operator A(){ return A(); }
// ^^^
can't compile. That's why the error you get is
error: no matching function for call to A::A()
The code tries to call your conversion operator - but the body isn't valid.
Why the B(B&) ctor is called, instead of B(const B&), in the construction of object b1 ?
#include <iostream>
using namespace std;
struct B
{
int i;
B() : i(2) { }
B(B& x) : i(x.i) { cout << "Copy constructor B(B&), i = " << i << endl; }
B(const B& x) : i(x.i) { cout << "Copy constructor B(const B&), i = " << i << endl; }
};
int main()
{
B b;
B b1(b);
}
This is because overload resolution applies, and since the argument to the constructor of b1 is b, and b happens to be non-const lvalue, then the constructor taking non-const lvlalue is selected. And that's the first one. Interestingly, both are copy constructors, but your code would be equaly valid with just the latter one.
Because b is not const. Therefore, it matches the first copy ctor perfectly, so that's what the compiler uses.
13.3.3.2/3 says
Two implicit conversion sequences of the same form are indistinguishable conversion sequences unless one
of the following rules apply:
— Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence
S2 if :
S1 and S2 are reference bindings (8.5.3), and the types to which the references refer are the same
type except for top-level cv-qualifiers, and the type to which the reference initialized by S2 refers is
more cv-qualified than the type to which the reference initialized by S1 refers. [Example:
int f(const int &);
int f(int &);
...
int i;
int j = f(i); // calls f(int&)
In your case since the argument is non-const, the non-const version of the copy c-tor is chosen because it is a better match.
because b is not a constant.
Try this:
int main() {
const B b;
B b1(b);
}
Also, it's a hard decision wheter you should use const or not ;)