Why does a function parameter call a constructor with an argument? - c++

I have this code(stolen from cplusplus.com):
// explicit:
#include <iostream>
using namespace std;
class A {};
class B {
public:
explicit B (const A& x) {}
B& operator= (const A& x) {return *this;}
operator A() {return A();}
};
void fn (B x) {}
int main ()
{
A foo;
B bar (foo);
bar = foo;
foo = bar;
// fn (foo); // not allowed for explicit ctor.
fn (bar);
return 0;
}
So apparently the statement fn (foo); would call the constructor defined in class B, and I do not really get why. Why would that call a constructor with an argument, I mean aren't things simply copied to the function parameters when not using a reference? If fn (foo); calls the constructor B (const A& x) {}, shouldn't fn (bar); generate an error since there is no constructor defined for an argument of type B, such as B (const B& x) {}?

It's all because of fn()'s signature:
void fn (B x) {}
fn only accepts objects of type B, and passes them by value.
So what happens when calling fn() with an object of type A?
A foo;
fn (foo); // not allowed for explicit ctor.
The compiler tries to find the best matching function - a function called fn and receives type A. (Overload resolution)
Because there is no such function, it then tries to find the next best match by casting the object to a type that fn() accepts.
A conversion constructor would usually do the job : B (const A& x) {}
But because it's marked with an explicit specifier the compiler can't do it implicitly.
You would have to explicitly cast the object for the compiler to find a match:
fn ((B)foo); // Explicit conversion is allowed.

That is a result of an explicit creation of an object
fn(foo);
is not implicitly possible since fn() doesn't takes any A object as parameter
BUT your foo parameter can actually be used to create an instance of B which match perfectly the fn parameter, you can prevent that implicitly conversion of objects using the explicit keyword.
And that is what you are doing here:
explicit B (const A& x) {}

From the C++ Standard
15 The initialization that occurs in the = form of a brace-or-equal-initializer or condition (6.4), as well as in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and aggregate member initialization (8.6.1), is called copy-initialization. [ Note: Copy-nitialization may invoke a move (12.8).—end note ]
Thus for the explicit constructor
explicit B (const A& x) {}
this initialization that in fact is used for the initialization of the function parameter
A foo;
B bar = foo;
is wrong.
It is the direct initialization that can be used with the explicit constructor as it is shown in your program
A foo;
B bar( foo );

Well ... B(const B&) although not explicitly declared, is there. That's because it is one of the special functions for which the compiler has a default:
B()
B(const B&)
B(B&&)
B& operator=(const B&)
B& operator==(B&&)
~B()
So, B can copy itself.
It cannot convert implicitly form A being such a conversion is declared as explicit.
fn(B(foo)) would have worked.

Related

How to initialize parameters / return values whose type has no copy constructor and only explicit constructors

How to initialize function parameters or function return values when the type has no copy constructor and only explicit constructors? For example:
struct A { };
struct B {
B(const B&) = delete;
explicit B(const A&) {}
};
void foo(B) {}
B bar() {
A a;
return ...; // how to return a?
}
int main() {
A a;
foo( ... ); // how to pass a?
}
return a / foo(a) does not work because constructor B(const A&) is explicit. return B(a) / foo(B(a)) does not work because copy constructor is deleted.
My intention is to know the language rules. To me it looks like a flaw in the language that, considering the presented type B, I can initialize locals, statics and class members but apparently neither function parameters nor function return values.
B(const B&) = delete;, without an explicit definition of move-constructor, means the object cannot be copied or moved.
This means that you cannot pass by value, either as a parameter or as a return.
The explicit constructor is a red herring.
If you don't want to change B to enable copying or moving, then you will have to pass by reference.
answers in annotated code below:
struct A { };
struct B {
B(const B&) = delete;
B(B&&) = default; // how to return B - pt 1 : move constructor
explicit B(const A&) {}
};
void foo(B) {}
B bar() {
A a;
return B(a); // how to return B - pt 2 : explcitly convert it
}
int main() {
A a;
foo(B(a)); // how to pass a - explicit conversion
}
I think your issue is a misconception:
You do not initialize function parameters or return values with the constructor.
Instead you hand in parameters to a function, handing in means copying them.
Depending on the function signature either the variable itself is copied (void foo(B b)) or the reference/pointer to it is copied.
In the first case you need a copy constuctor for your type, while you do not need one for references/pointers.
The same is true for returning values (!) from a function.
You also need a copy constructor there.
But you deleted the copy constructor, thus the compile errors.
With C++11 move semantics were added as an optimisation opportunity.
When a move constructor is available r-values will be moved in/out of functions automatically instead of copied.
Returning a local variable will also move it out instead of copying.
Since you declared a copy consturctor (no matter if as deleted/default) no move related functions will be created by the compiler.
And thus also the automatic moving fails.

Why it works when I assign the object with others' object?

I'd like to assign the object of type B to object of type A, but i don't know why it works with different types for the assignment?
#include <stdio.h>
class B
{
public:
B()
{
printf("B default constructor.\n");
}
};
class A
{
public:
A()
{
printf("A Default constructor.\n");
}
A(B const& b) // if add the tag "explicit" for the constructor, it will not work...
{
printf("User constructor.\n");
}
A(const A& a)
{
printf("copy-constructor.\n");
}
void get(){printf("A::get\n");}
};
int main()
{
A a = B(); // What's the meaning to assign object of type B to object of type A?
Why it works with above line?
How it works when do this?
a.get();
}
Every constructor that can be called with a single argument defines an implicit conversion to a class type. So the constructor:
A(B const& b)
is a conversion constructor. If this type of conversion is not useful, you've found the answer: declaring it as explicit can prevent it:
explicit A(B const& b)
I think your problem comes from thinking it is assignment. But A a = B(); is initialization. a is not first created with default constructor and then assigned to, it is directly constructed. And while I don't have the standard reference handy, if you don't have the constructor but have the assignment operator overload, that line will not compile, because it is not assignemnt, it needs the right constructor. To have assignment, try
A a;
a = B(); // actually assigns A(B()) implicitly if no operator= overload
If all data members of a class can be assigned, then objects of the class can be assigned, so that's why a = A(); works without adding any code.
If you want to block the implicit conversion, make the constructor explicit (example in the other answer).

what is happened for constructors that accept objects as parameters ?

i want ask what is actually meaning by objects conversion , for example:
why i can't access print at class B using foo after assignment it to A ?!
#include <iostream>
using namespace std;
class A {};
class B {
public:
// conversion from A (constructor):
B (const A& x) {}
void print(){cout << "Huo Jackman!\n";}
};
int main ()
{
A foo;
B bar = foo; // calls constructor
foo.print();
return 0;
}
Use:
bar.print();
Please note that the original foo object is not changed when bar is created. foo has type class A, which doesn't contain a print method, and in C++ it's not possible to add methods on the fly.
Your code is an example of Type Conversion. By doing the following
B (const A& x) {}
You are constructing a new object of Type B from an object of Type A. Here the original object A remains unchanged. When you write
A foo;
B bar = foo; // calls constructor
You have first created foo which is an object of Type A. From this object you are now creating an object of Type B taking foo as an argument. It will construct bar but will leave foo unchanged. That is why you cannot invoke the print method on foo since the print method is defined only for bar and not foo.
Now as you asked what would you gain by doing this.
Consider the following code
void fun ( B& b )
{
// do something with object b
}
void main ()
{
A a;
fun ( a );
}
The function fun here is expecting an object of type B but it is being passed an object of Type A. Before generating compile error, the compiler will try to see if there is any way it can construct object of Type B from an object of Type A. If you had not defined any conversion, the compiler will generate compile error. But here since we can construct an object of type B taking A as an argument, the compiler will do that and the call will succeed. This type of mechanism does have some unintended consequences. Here you wanted to call the function fun with object of type B but the call succeeded even with object of type A since compiler does an implicit conversion from Type A to Type B using the constructor in B. To stop this kind of behavior, keyword 'explicit' can be used on the affected constructor.
explicit B (const A& x) {}
Consider the following code inside class B
B& operator= (const A& x) {return *this;}
This will not create any new object. However it will be used modify an object of type B using an object of type A. If on the other hand we want to construct an object of Type A from an object of Type B we will have to use a typecast operator ( conversion function) in class B:
operator A() { return A(); }
So the class B would look something like:
class B
{
public:
B ( const A& a ) {} // conversion from A using constructor
B& operator= ( const A& a ) { return *this;} // conversion from A using assignment
operator A() { return A(); } // conversion to A using typecast operator
};
int main ( int argc, char** argv )
{
A a;
B b= a; // Construct B from A using constructor ( 1st one above )
b = a; // copy a into b using assignment operator ( 2nd one above)
a = b; // construct A from B using typecast operator ( 3rd one above )
return 0;
}
Please refer to this and this for some additional information

using this pointer in initializer list

struct T
{
int a;
};
struct C
{
T& r;
C(T& v) : r(v) {}
};
struct E : T
{
T& r;
E(T const& v) : r(*this), T(v) {} // ok
};
struct F : C, T // base order doesn't matter here
{
//F(T const& v) : C(*this), T(v) {} // error : C::r is not initialized properly
F(T const& v) : C(*static_cast<T*>(this)), T(v) {} // ok
//F(T const& v) : C(static_cast<T&>(*this)), T(v) {} // ok
};
int main()
{
T v;
F f(v);
f.r.a = 1;
}
Although using this pointer in initializer list could be problem, but I've never expected this happened to PODs and may be simply fixed by explicit cast;
Is this some compiler bug or std related problem?
The code is ambiguous.
For constructing the C base of F, the context is direct-initialization, so 13.3.1.3 applies:
c++11
13.3.1.3 Initialization by constructor [over.match.ctor]
For direct-initialization, the candidate
functions are all the constructors of the class of the object being initialized.
The implicitly-declared copy constructor is included, per 12.8:8.
The candidates for the constructor of C are C(T &) and (the default copy constructor) C(const C &), by parameter list (F). In both cases we have a reference binding (13.3.3.1.4) followed by a derived-to-base Conversion (13.3.3.1), with an additional cv-qualification adjustment in the latter case, giving overall rank of Conversion in both cases.
Since C and T are both base classes of F, but are distinct types and neither is a base class of the other, none of the clauses in 13.3.3.2:3 nor 13.3.3.2:4 apply and conversion sequences are indistinguishable.
Indeed, gcc-4.5.1 rejects the code with:
prog.cpp: In constructor 'F::F(const T&)':
prog.cpp:20:34: error: call of overloaded 'C(F&)' is ambiguous
prog.cpp:9:5: note: candidates are: C::C(T&)
prog.cpp:7:1: note: C::C(const C&)
When you try to initialize the base C of F with *this, both the compiler generated copy constructor for C and the constructor that you define taking a T& are a match as the type of *this (F) is derived directly from both C and T. Your cast resolves this ambiguity.
I am surprised that the copy constructor is a better match than the one taking T& as I would have thought that they would both be equally preferred. If the copy-constructor is chosen then the base will be initialized from itself which causes undefined behavior as the reference member will be initialized from an uninitialized reference (itself).
1)never use this in initialization list
http://msdn.microsoft.com/en-us/library/3c594ae3(v=vs.80).aspx
The this pointer is valid only within nonstatic member functions. It cannot be used in the initializer list for a base class.
The base-class constructors and class member constructors are called before this constructor. In effect, you've passed a pointer to an unconstructed object to another constructor. If those other constructors access any members or call member functions on this, the result will be undefined. You should not use the this pointer until all construction has completed.
shortly: C.r initialized by bad poiter
struct C
{
T& r;
C(T& v) : r(v) {}
};
struct E : T
{
T& r;
E(T const& v) : r(*this), T(v) {} // ok
};
You need to initialize any reference at the time of declaration but here you have just declared it. This is not allowed in C++.

c++ is this copy constructor?

class A {};
class B { public: B (A a) {} };
A a;
B b=a;
I read this from http://www.cplusplus.com/doc/tutorial/typecasting/ . It says this is a implicit type conversion. From class A to class B.
I want to ask, is this also an example of copy constructor?
Thanks.
No, it's not a copy constructor. A copy constructor copies one object of one type into another of the same type:
B::B(const B& b)
{
// ...
}
As a side note, if you need a copy constructor then you also need a destructor and an assignment operator, and probably a swap function.
What B::B(A) is is a conversion function. It's a constructor that allows you to convert an object of type A into an object of type B.
void f(const B& obj);
void g()
{
A obja;
B objb = obja;
f(obja);
}
No, A copy constructor has the form
class A
{
public:
A(const A& in) {...}
}
No, a copy constructor is called when you create a new variable from an object. What you have there is two objects of different types.
The line B b = a; implies that a copy constructor is used, as if you had typed B b = B(a); or B b((B(a)));. That is, the compiler will check whether B has an accessible (public) copy constructor - whether user-defined or the default one provided by the compiler. It doesn't mean, though, that the copy constructor has to be actually called, because the language allows compilers to optimize away redundant calls to constructors.
By adding a user-defined copy constructor to B and making it inaccessible, the same code should produce a compiler error:
class A {};
class B {
public:
B (A ) {}
private:
B (const B&) {} // <- this is the copy constructor
};
A a;
B b=a;
For example, Comeau says:
"ComeauTest.c", line 10: error: "B::B(const B &)" (declared at line 6), required
for copy that was eliminated, is inaccessible
B b=a;
^