C++ 构造函数和复制构造函数
c++ constructor and copy constructor
我正在尝试理解以下代码的行为
/* 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
当我从复制构造函数中删除常量时
/* 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)初始化
我对编译器从t.cpp:9:4的输出感到困惑
从输出T.cpp:10:4,编译器说它需要一个 L 值复制构造函数,所以我将代码更改为
/* 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 中)编译器说它需要一个 rvalue copy 构造函数(来自代码块 4 -> t.cpp:10:4),所以我定义了一个,但正在运行的输出显示没有调用 rvalue copy 构造函数,为什么?
您所看到的在 C++17 之前的编译器中称为复制省略(在编译器资源管理器或 wandbox 上使用 C++-std=c++17
vs.-std=c++14
标志)。从 C++17 开始,编译器需要消除许多复制和移动构造函数的情况,并直接构造对象而不带任何中间对象。
与
A a { 10 };
行
A a = 10;
表示首先构造一个临时对象,就好像代码具有:
A a = A(10);
直到 C++17 年,编译器被允许优化此代码,并直接从10
构造a
,而无需临时对象。请注意,重点是允许但不要求执行此复制省略优化。您已经观察到这允许优化。
编译器必须编译或失败代码,而不管它是否决定执行复制省略。如果编译器无法调用复制构造函数(如您的情况),那么它必须无条件地使编译失败,即使它决定执行复制省略。这在 C++17 中发生了变化,在这种情况下,现在需要编译器进行复制省略优化。由于它保证了复制构造函数,因此甚至不需要复制构造函数,并且代码可以编译而不会出错。
请注意没有 const 的复制构造函数:
A(A &other) { std::cout << " other " << std::endl; value = other.value; }
如果没有复制省略,此复制构造函数不能用于:
A a = A(10);
它不能使用,因为 A(10) 是一个临时对象,因此可以作为 rvalue 参数传递给构造函数和方法,例如
A(A && other);
foo(A && other);
或作为 const lvalue 引用参数传递给构造函数和方法,例如
A(const A& other);
bar(const A& other);
但它不能作为常规可变参数传递(如在代码块 3 中)。
使用复制 elision,在这些情况下,它甚至不会尝试调用副本或移动构造函数。
它仍然需要调用复制构造函数
A b = a;
它可以使用可变参数来做到这一点,只是因为a
既不是临时对象也不是 const 对象。如果您使用 consta
则当复制构造函数未获得 const 时,代码将无法编译(对于 C++17 及更早版本):
const A a = 10;
A b = a;
// ^^ this will fail
有趣的说明:以下行将保证不会调用复制构造函数,甚至不会使用 C++17 调用一次:
A a = A(A(A(A(1))));
当你写的时候
A a = 10;
编译器将 10 转换为临时对象,然后调用复制构造函数来创建 a。
A a = A(10);
考虑这个程序,
#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 lvalue" << std::endl; value = other.value; }
void print(){ std::cout << "print " << value << std::endl; }
};
int main(int argc, char **argv)
{
A a = 10;
//A a(1);
//A b = a;
//b.print();
return 0;
}
并编译它
g++ t.cpp -std=c++11
在运行程序时,其输出为
int n
现在您可能想知道为什么不调用复制构造函数A(const A &other)
。这是因为C++中的复制省略。编译器可以优化对复制构造函数的调用,并直接调用匹配的构造函数。 因此,与其A a = A(10);
,不如说是这个A a(10);
如果要禁用复制省略,请使用
g++ t.cpp -std=c++11 -fno-elide-constructors
现在运行程序,您可以看到下面的输出
int n
other
没有复制省略。所以,A a = A(10);
被召唤了。首先创建一个临时对象,然后调用复制构造函数来创建a
。
(in code block 3) why can't I remove the const from copy constructor?
因为临时对象不能绑定到左值引用。它们只能绑定到右值引用或常量左值引用。A(10)
创建一个临时对象,该对象只能绑定到常量左值引用(const A&)或右值引用(A&&)。
(in code block 5) the compiler says that it need a rvalue copy constructor(from code block 4 -> t.cpp:10:4), so I define one, but the running output show the rvalue copy constructor wasn't called, why?
发生这种情况是因为复制省略。使用-fno-elide-constructors
编译它,然后可以看到对 rvalue 构造函数的调用。见下文。
#include <iostream>
class A
{
private:
int value;
public:
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 lvalue" << std::endl; value = other.value; }
void print(){ std::cout << "print " << value << std::endl; }
};
int main(int argc, char **argv)
{
A a = 10;
//A a(1);
//A b = a;
//b.print();
return 0;
}
编译:
g++ t.cpp -std=c++11 -fno-elide-constructors
输出
int n
other lvalue
代码运行时
A a = 10;
它将 10 强制转换为类型 A 的变量,并调用复制构造函数来初始化"a"。因为 10 在内存中没有引用,所以它是一个右值。由于您的复制构造函数通过引用接收"其他",因此您无法传入右值,因为如果修改了,则没有要修改的引用。C++ 仅当通过 const 引用时才允许通过引用传入右值。
它试图将其强制转换为 int 的原因是你只有两个构造函数:一个接受 int,另一个通过引用接收类型为"A"的对象。由于它已经将 10 强制转换为类型为"A"的对象,并且该对象是右值,因此它只是声明两个构造函数都不能接受该对象。
请注意:这是一个提醒,在对象未被修改时始终通过 const 引用。
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- 当从函数参数中的临时值调用复制构造函数时
- 如果有一个模板构造函数只有一个泛型参数,为什么我必须有一个复制构造函数
- 使用复制构造函数复制双精度数组
- C 无可行的构造函数复制类型的变量
- 没有可行的构造函数复制类型 'MyString' 的数组元素
- 编译时,复制构造函数/复制分配和正常功能调用优化之间是否存在任何区别
- 如何最小化调用列表构造函数(复制构造函数)的次数?
- C 11矢量构造函数复制与范围
- 我定义了一个非复制构造函数;复制构造函数还会被隐式定义吗
- 可以将构造函数复制为转换运算符
- 将基类指针的构造函数复制到子类
- C++树类:构造函数/复制/内存泄漏
- 如何制作这个在模板构造函数复制中使用类型定义的类型的模板
- 将构造函数复制为模板化的成员函数
- 绕过私有复制构造函数/复制赋值C++
- C++通过构造函数复制对象
- 复制构造函数 - 复制C++中的对象
- 将带unique_ptr的类的构造函数复制到作为成员的抽象类