Why does a function parameter call a constructor with an argument?
I have this code (stolen by 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 obviously the operator fn (foo);
will call the constructor defined in class B, and I don't really understand why. Why would this call the constructor with an argument, I mean things aren't just copied into function parameters when no references are used? If fn (foo);
calls a constructor B (const A& x) {}
, should not fn (bar);
throw an error as there is no constructor for type B argument such as B (const B& x) {}
?
All this because of the fn()
signature:
void fn (B x) {}
fn only takes objects of type B and passes them by value.
So what happens when you call fn () with an object of type A?
A foo;
fn (foo); // not allowed for explicit ctor.
The compiler tries to find the best match function, a function called fn, and gets type A. ( Overload resolution )
Since there is no such function, it then tries to find the next best match by subjecting the object to the type that fn () accepts.
A conversion constructor usually does the job: B (const A& x) {}
But since it is marked explicit , the compiler cannot do it implicitly.
You need to explicitly specify the object for the compiler to match:
fn ((B)foo); // Explicit conversion is allowed.
source to share
This is the result of explicit object creation
fn(foo);
implicitly possible, since it fn()
does not accept any object A as a parameter
BUT your foo parameter can actually be used to create an instance of B that matches the fn parameter perfectly , you can prevent this implicit object conversion using the Explicit keyword .
And this is what you are doing here:
explicit B (const A& x) {}
source to share
Well ... B(const B&)
although not explicitly announced, there is. This is 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 implicitly convert the form A, since such a conversion is declared explicit.
fn(B(foo))
would work.
source to share
From the C ++ standard
15 Initialization, which occurs in the form = / of an element with an equal or equal-initializer or condition (6.4), and , as when passing arguments , returns a function, throwing an exception (15.1), handling an exception (15.3), and initializing an aggregate member (8.6 .1) is called copy-initialization . [Note: copy-initialization may cause movement (12.8). - End note]
So for an explicit constructor
explicit B (const A& x) {}
this initialization which is actually used to initialize the function parameter
A foo;
B bar = foo;
wrong.
This is a direct initialization that can be used with an explicit constructor as shown in your program
A foo;
B bar( foo );
source to share