The following code throws compilation error:
#include <stdio.h>
class Option
{
Option() { printf("Option()\n"); };
public:
explicit Option(const Option& other)
{
printf("Option(const)\n");
*this = other;
}
explicit Option(Option& other)
{
printf("Option(non-const)\n");
*this = other;
}
explicit Option(const int&)
{
printf("Option(value)\n");
}
};
void foo(Option someval) {};
int main()
{
int val = 1;
Option x(val);
foo(x);
}
The error thrown is:
main.cpp:31:10: error: no matching function for call to ‘Option::Option(Option&)’
foo(x);
^
main.cpp:5:5: note: candidate: ‘Option::Option()’
Option() { printf("Option()\n"); };
^~~~~~
main.cpp:5:5: note: candidate expects 0 arguments, 1 provided
main.cpp:25:6: note: initializing argument 1 of ‘void foo(Option)’
void foo(Option someval)
The error goes away if I remove the explicit keyword from explicit Option(const Option& other)
Can someone explain to me what is the cause of compilation error?
Also, if there a difference between explicit Option(const Option& other)
and explicit Option(Option& other)
?
In the call
foo(x)
, a newOption
has to be created, which will becomesomeVal
during the execution offoo
's body. That is,x
needs to be copied intosomeVal
. The compiler essentially tries to initializeOption someVal(x);
(trying firstOption(Option&)
, thenOption(Option const&)
), but it cannot, because you said that both of those constructors areexplicit
and should not be implicitly called. With C++17, you can explicitly insert the missing constructor call to make it work:foo(Option(x))
. Before C++17, it is impossible to callfoo
, because the compiler will keep trying to insert calls to a constructor toOption
but none are available for insertion.In the language of the standard, a function call like
foo(x)
calls for the parametersomeVal
to be copy-initialized fromx
. Copy-initializing an object of a certain class from an object of that class or a derived class considers only the converting constructors of that destination class. "Converting constructor" is just a fancy name for "constructor that isn'texplicit
". The best constructor out of these is then selected by normal overload resolution. Since none of your constructors are notexplicit
, this always fails andfoo
is uncallable before C++17. Since C++17, when the argument is a prvalue (as infoo(Option(x))
), the requirement to call a constructor can be sidestepped andfoo
becomes callable.To your side question:
Of course: the first one promises it will not modify its argument and the second one doesn't. You already know that they can be defined to do different things, and overload resolution will prefer one over the other, depending on context: