“explicit”关键字如何影响 C++ 复制构造函数和函数参数?

And*_*rés 4 c++ compiler-errors explicit copy-elision elision

修改复制构造函数的“ explicit ”关键字可能会导致问题。\n作为函数参数传递的对象特别容易受到这些问题的影响。

\n

这是我的代码:

\n
#include <iostream>\n#include <string>\n\nclass Pig{\npublic:\n    std::string _name;\npublic:\n    Pig(std::string n) : _name(n) {}\n    //~Pig() = default;\n    explicit Pig(const Pig &other) {\n        std::cout << "copy ctor!" << std::endl;\n        this->_name = other._name;\n    }\n};\n\nvoid show(Pig p) {\n    std::cout << p._name << std::endl;\n}\n\nint main() {\n    Pig pig{std::string("hello")};\n    show(Pig{pig});     // no pass\n    // show(Pig(pig));  // no pass\n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

编译器版本:g++(Ubuntu 9.4.0-1ubuntu1~20.04.1)9.4.0。

\n

上面提到的代码不能用c++14 或更低版本编译,\n但用c++17 及更高版本编译成功。

\n

这是编译器的错误:

\n
test.cpp: In function \xe2\x80\x98int main()\xe2\x80\x99:\ntest.cpp:22:7: error: cannot convert \xe2\x80\x98Pig\xe2\x80\x99 to \xe2\x80\x98std::string\xe2\x80\x99 {aka \xe2\x80\x98std::__cxx11::basic_string<char>\xe2\x80\x99}\n   22 |  show(Pig{pig});     // \xe4\xb8\x8d\xe9\x80\x9a\xe8\xbf\x87\n      |       ^~~~~~~~\n      |       |\n      |       Pig\n
Run Code Online (Sandbox Code Playgroud)\n
    \n
  • 我知道如何使用显式,并且我想显式调用复制构造函数以使其工作。
  • \n
\n

提前致谢!

\n

我尝试使用 c++14 和 c++17 进行编译。

\n

use*_*570 5

先前的 C++17

问题是,在 C++17 之前,当将参数作为参数传递给函数时,会有参数的概念副本。也就是说,函数的命名参数是从传递的参数中复制初始化的,并且由于复制构造函数被标记,这会给您带来提到的错误。Pig{pig}showpshowPig{pig}explicit

这可以从复制初始化看出:

复制初始化在以下情况下执行:

  • 按值将参数传递给函数时

(强调我的)


C++17

OTOH 从 C++17 开始,纯右值 直接构造Pig{pig}到的存储中。也就是说, C++17 中的参数没有概念性的副本,因此复制构造函数不需要是非显式的等。p

从 C++17 开始,我们有强制复制省略

在以下情况下,编译器需要省略类对象的复制和移动构造,即使复制/移动构造函数和析构函数具有可观察到的副作用。这些对象直接构造到存储中,否则它们将被复制/移动到存储中。复制/移动构造函数不需要存在或可访问

  • 在对象的初始化中,当初始化表达式是与变量类型相同的类类型(忽略 cv 限定)的纯右值时:

(强调我的)

请注意,这Pig{pig}是一个纯右值,因此上述内容适用,并且复制构造函数不需要存在或可访问(并且它可以是显式的)。