提问人:Andrés 提问时间:5/24/2023 最后编辑:user12002570Andrés 更新时间:5/25/2023 访问量:102
“explicit”关键字如何影响 C++ 复制构造函数和函数参数?
How does the 'explicit' keyword affect C++ copy constructors and function parameters?
问:
修改复制构造函数的“explicit”关键字可能会导致问题。 作为函数参数传递的对象特别容易受到这些问题的影响。
这是我的代码:
#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;
}
编译器版本:g++ (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0。
上面提到的代码不能用 c++14 或更低版本编译, 但在 C++17 及更高版本中成功编译。
以下是编译器的错误:
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
- 我知道如何使用显式,我想显式调用复制构造函数以使其工作。
提前致谢!
我尝试使用 c++14 和 c++17 进行编译。
答:
5赞
user12002570
5/24/2023
#1
先前的 C++17
问题是,在 C++17 之前,当将参数作为参数传递给函数时,将有参数的概念副本。也就是说,函数命名的参数是从传递的参数复制初始化的,并且由于复制 ctor 被标记,因此这为您提供了上述错误。Pig{pig}
show
p
show
Pig{pig}
explicit
这可以从副本初始化中看出:
在以下情况下执行复制初始化:
- 按值将参数传递给函数时。
(强调我的)
C++17
OTOH 从 C++17 开始,prvalue 直接构造到 的存储中。也就是说,没有来自 C++17 的参数的概念副本,因此副本 ctor 不需要是非显式的等。Pig{pig}
p
从 C++17 开始,我们有强制性的复制省略:
在以下情况下,编译器需要省略类对象的复制和移动构造,即使复制/移动构造函数和析构函数具有可观察到的副作用。这些对象被直接构造到存储中,否则它们将被复制/移动到该存储中。复制/移动构造函数不需要存在或可访问:
- 在对象的初始化中,当初始值设定项表达式是与变量类型相同类类型(忽略 cv-qualification)的 prvalue:
(强调我的)
请注意,这是一个 prvalue,因此上述内容适用,复制构造函数不需要存在或可访问(并且可以是显式的)。Pig{pig}
评论
void show(Pig const& p)
Pig
Pig{pig}