如何避免在 c++11 中触发这种复制构造函数?
How to avoid triggering this kind of copy constructor in c++11?
我想在函数中创建一个对象并在外面使用它。
我在 c++17 标准下编写以下代码,似乎还可以。
#include <iostream>
struct Vector2 {
Vector2() = default;
Vector2(int x, int y) : x(x), y(y) {}
Vector2(const Vector2 &) = delete;
Vector2 &operator=(const Vector2 &) = delete;
int x = 0;
int y = 0;
};
Vector2 newVec(int x, int y) {
return Vector2(x, y);
}
int main() {
auto v = newVec(1, 2);
std::cout << v.x * v.y << std::endl;
return 0;
}
但是当我切换到 c++11 标准时,我无法编译它。
注意:"Vector2"已在此处明确标记为已删除 Vector2(const Vector2 &) = delete;
我想我在newVec函数中构造了一个临时的Vector2对象。返回时,Vector2 调用复制构造函数,并将临时变量作为参数。但是我已经将复制构造函数标记为"删除",因此无法编译。
我的问题是 C++11 标准下的等效代码是什么? c++17 做了什么来避免这个复制构造函数?
c++17 做了什么来避免这个复制构造函数?
这:
auto v = newVec(1, 2);
是名为"保证复制消除"的语言功能的激励案例之一。该论文中的示例:
auto x = make(); // error, can't perform the move you didn't want, // even though compiler would not actually call it
在 C++17 之前,该表达式始终复制初始化。我们用auto
推导出类型,这给了我们Vector2
,然后我们试图从类型Vector2
的右值构造一个Vector2
。这是通常的过程 - 枚举构造函数等。最佳匹配是你的复制构造函数,它被删除了,因此整个事情的格式不正确。
悲伤的脸。
在 C++17 中,这是完全不同的。从相同类型的 prvalue 初始化根本不会尝试查找构造函数。没有复制或移动。它只是给你一个Vector2
,即newVec(1, 2)
的价值,直接。这就是这里的变化 - 并不是说在 C++17 中复制初始化有效,而是它甚至不再是同一种初始化。
我的问题是 C++11 标准下的等效代码是什么?
根本没有办法构造这样的不可移动物体。来自同一篇论文:
struct NonMoveable { /* ... */ }; NonMoveable make() { /* how to make this work without a copy? */ }
如果您希望像make()
这样的函数(在您的示例中newVec()
)工作,则该类型至少必须是可移动的。这意味着:
- 添加移动构造函数
- 取消删除复制构造函数
- 如果以上都不可能,请将其放在堆上 - 将其包裹在类似
unique_ptr<Vector2>
的东西中,这是可移动的
或者,你传入你的对象make()
并让函数在内部填充它:
void make(NonMoveable&);
这最终会降低作曲能力,但它有效。如前所述,它要求你的类型是默认的可构造的,但也有一些解决方法。
您在 C++17 中观察到保证副本省略。
另一方面,C++11 要求复制或移动构造函数存在且可访问,即使编译器最终决定避免其调用也是如此。
您需要删除复制构造函数的显式删除,以便生成移动构造函数(但复制构造函数也是如此),或者像这样声明移动构造函数:
Vector2(Vector2 &&) = default;
根据标准,
如果类 X 的定义没有显式声明移动 构造函数,一个将被隐式声明为默认值,当且仅 如果
— X 没有用户声明的复制构造函数,
— X 没有用户声明的复制赋值运算符,
— X 没有用户声明的移动分配运算符,并且
— X 没有用户声明的析构函数。
用户声明是指用户提供的(由用户定义)、显式默认(= 默认)或显式删除(= 删除)
另一方面,在 C++17 中,由于此处描述的原因,省略了对复制/移动构造函数的调用:
在以下情况下,即使复制/移动(自 C++11)构造函数和析构函数具有可观察到的副作用,编译器也允许,但不要求省略类对象的复制和移动(自 C++11 起)构造。这些对象直接构造到存储中,否则它们将被复制/移动到存储中。
。
(自 C++17) 在对象的初始化中,当源对象是无名临时对象并且与目标对象属于相同的类类型(忽略 cv-quality)时。当无名临时是 return 语句的操作数时,这种复制省略的变体称为 RVO,即"返回值优化"。 (至C++17)
返回值优化是强制性的,不再被视为复制省略;见上文。
您可以通过两个小更改来修复它:
return {x, y};
来自newVec()
. 这将只构造对象一次,不需要复制构造函数。const auto& v
而不是auto v
. 这将具有相同的生存期(但不能修改它),并且不需要复制构造函数。
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- 当从函数参数中的临时值调用复制构造函数时
- 如果有一个模板构造函数只有一个泛型参数,为什么我必须有一个复制构造函数
- 为什么需要复制构造函数,在哪些情况下它们非常有用
- 使用仅使用一次的变量调用的复制构造函数.这可能是通过调用move构造函数进行编译器优化的情况吗
- 为什么类中的ostringstream类型的成员会导致";调用隐含删除复制构造函数";错误
- 复制构造函数、赋值运算符C++
- std::ofstream 作为类成员删除复制构造函数?
- 复制构造函数C++无法正确复制指针
- 关于复制构造函数的一个棘手问题
- 为什么调用复制构造函数而不是移动构造函数?
- 填充上编译器生成的复制构造函数之间的不一致
- C++ 对象指针数组的复制构造函数
- C++ 基本 CTOR 说明 - 为什么不调用赋值/复制构造函数
- 防止在复制构造函数中隐式调用基构造函数
- 为用户定义的类正确调用复制构造函数/赋值运算符
- 具有已删除移动和复制构造函数的类的就地构造
- 复制构造函数隐式转换问题
- 复制构造函数中的递归调用