c++ - c++构造函数和复制构造函数
问题描述
我试图了解以下代码的行为
/* code block 1 */
#include <iostream>
class A
{
private:
int value;
public:
A(int n) { std::cout << "int n " << std::endl; value = n; }
A(const A &other) { std::cout << " other " << std::endl; value = other.value; }
// A (A &&other) { std::cout << "other rvalue" << std::endl; value = other.value; }
void print(){ std::cout << "print " << value << std::endl; }
};
int main(int argc, char **argv)
{
A a = 10;
A b = a;
b.print();
return 0;
}
当我编译上面的代码时,它可以按我的预期工作
/* code block 2 */
g++ -std=c++11 t.cpp
./a.out
int n
other
print 10
当我从复制构造函数中删除const
/* code block 3 */
class A
{
...
A(int n) { std::cout << "int n " << std::endl; value = n; }
A(A &other) { std::cout << " other " << std::endl; value = other.value; }
// A (A &&other) { std::cout << "other rvalue" << std::endl; value = other.value; }
}
编译器不会编译
/* code block 4 */
t.cpp:19:5: error: no viable constructor copying variable of type 'A'
A a = 10;
^ ~~
t.cpp:9:4: note: candidate constructor not viable: no known conversion from 'A' to 'int' for 1st argument
A(int n) { std::cout << "int n " << std::endl; value = n; }
^
t.cpp:10:4: note: candidate constructor not viable: expects an l-value for 1st argument
A(A &other) { std::cout << " other " << std::endl; value = other.value; }
从结果t.cpp:9:4看来,编译器试图将 A 转换为 int,但代码是A a = 10; , 如果我是编译器,我会
试图从整数10初始化一个类型为 A 的临时变量,然后使用复制构造函数A(A &other)来初始化一个
直接用构造函数A(int)初始化a
我对t.cpp:9:4的编译器输出感到困惑
从输出t.cpp:10:4,编译器说它需要一个左值复制构造函数,所以我将代码更改为
/* code block 5 */
class A
{
...
A(int n) { std::cout << "int n " << std::endl; value = n; }
A(A &other) { std::cout << " other " << std::endl; value = other.value; }
A (A &&other) { std::cout << "other rvalue" << std::endl; value = other.value; }
}
当我按照提示定义右值复制构造函数时,输出显示未调用右值复制构造函数
/* code block 6 */
g++ -std=c++11 t.cpp
int n
other
print 10
问题:
- (在代码块 3 中)为什么我不能从复制构造函数中删除const ?
- (在代码块 4 -> t.cpp:9:4 中)为什么编译器会尝试从 'A' 转换为 'int'?
- (在代码块 5 中)编译器说它需要一个右值复制构造函数(来自代码块 4 -> t.cpp:10:4),所以我定义了一个,但运行输出显示右值复制构造函数没有被调用, 为什么?
解决方案
您所看到的在 C++17 之前的编译器中称为复制省略(在编译器资源管理器中使用 C++17或带有vs.标志的wandbox进行尝试)。从 C++17 开始,编译器需要消除许多复制和移动构造函数的情况,并直接构造对象而无需任何中间对象。-std=c++17
-std=c++14
不像
A a { 10 };
线
A a = 10;
表示先构造一个临时对象,就好像代码有:
A a = A(10);
在 C++17 之前,允许编译器优化此代码,并a
直接从10
没有临时对象的情况下构造。请注意,重点是允许但不需要执行此复制省略优化。您已经观察到这种允许的优化。
无论编译器是否决定执行复制省略,编译器都必须编译或失败代码。如果编译器无法调用复制构造函数,就像您的情况一样,那么即使它决定进行复制省略,它也必须无条件地使编译失败。这在 C++17 中发生了变化,现在需要编译器在这种情况下进行复制省略优化。由于保证省略了复制构造函数,因此甚至不需要复制构造函数,并且代码可以无错误地编译。
注意没有 const 的复制构造函数:
A(A &other) { std::cout << " other " << std::endl; value = other.value; }
如果没有复制省略,则此复制构造函数不能用于:
A a = A(10);
它不能被使用,因为 A(10) 是一个临时对象,因此可以作为右值参数传递给构造函数和方法,例如
A(A && other);
foo(A && other);
或作为 const 左值引用参数传递给构造函数和方法,例如
A(const A& other);
bar(const A& other);
但它不能作为常规可变参数传递(如代码块 3 中)。
在这些情况下,使用复制省略它甚至不会尝试调用复制或移动构造函数。
它仍然需要调用复制构造函数
A b = a;
它可以使用可变参数来做到这一点,只是因为a
它既不是临时对象也不是 const 对象。如果你 make a
const 那么代码将无法编译,当复制构造函数没有得到一个 const 时(对于 C++17 和更早版本):
const A a = 10;
A b = a;
// ^^ this will fail
有趣的注释:以下行将保证即使使用 C++17 也不会调用复制构造函数:
A a = A(A(A(A(1))));
推荐阅读
- python - 在等于值的地方交换 cols 中的值 - Pandas
- windows - Windows 上的 Antlr 解析树视图
- java - 当数组结束时,它将如何进入下一个活动?
- java - 数组(小a)在没有import语句(import java.util.Arrays)的情况下在java中工作,这是为什么呢?
- apache-spark - 如何使用 Spark-shell 一次性从表的所有列中过滤掉所有空值?
- javascript - 如何将焦点从子窗口移回父窗口
- java - 黑莓无法使用 HTTPS 连接到某些网页
- sql - 如何使用列名作为过程参数来更新列值?
- java - Android Studio 错误:java.lang.IllegalArgumentException:没有这样的服务 ComponentInfo{com.example.notificationscheduler
- asp.net-core - 将客户端验证从.net core 2.0 转换为服务器端到 3.1