The "explicit" keyword to modify the copy constructor can cause problems. Objects passed as function parameters are particularly susceptible to these issues.
here are my codes:
#include <iostream>
#include <string>
class Pig{
public:
std::string _name;
public:
Pig(std::string n) : _name(n) {}
//~Pig() = default;
explicit Pig(const Pig &other) {
std::cout << "copy ctor!" << std::endl;
this->_name = other._name;
}
};
void show(Pig p) {
std::cout << p._name << std::endl;
}
int main() {
Pig pig{std::string("hello")};
show(Pig{pig}); // no pass
// show(Pig(pig)); // no pass
return 0;
}
compiler version: g++ (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0.
The code mentioned above does not compile with c++14 or lower versions, but compiles successfully with c++17 and later versions.
here are the compiler's errors:
test.cpp: In function ‘int main()’:
test.cpp:22:7: error: cannot convert ‘Pig’ to ‘std::string’ {aka ‘std::__cxx11::basic_string<char>’}
22 | show(Pig{pig}); // 不通过
| ^~~~~~~~
| |
| Pig
- I am aware of how to use explicit and I would like to call the copy constructor explicitly in order to make it work.
Thanks in advance!
I tried compiling with c++14 and c++17.
Prior C++17
The problem is that prior to C++17, there will be a conceptual copy of the argument
Pig{pig}
when passing it as an argument to theshow
function. That is, the parameter namedp
of theshow
function is copy initialized from the passed argumentPig{pig}
and since the copy ctor is markedexplicit
this gives you the mentioned error.This can be seen from copy initialization:
(emphasis mine)
C++17
OTOH starting with C++17 the prvalue
Pig{pig}
is constructed directly into the storage forp
. That is, there is no conceptual copy of the argument from C++17 and therefore no need for the copy ctor to be non-explicit etc.From C++17 we have mandatory copy elision:
(emphasis mine)
Note that
Pig{pig}
is a prvalue and so the above applies and the copy constructor doesn't need to be present or accessible(and it can be explicit).