我想在函数中创建对象并在外部使用它。
我按照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为避免此副本构造函数做了什么?
答案 0 :(得分:6)
c ++ 17为避免此副本构造函数做了什么?
此:
auto v = newVec(1, 2);
是一种被称为“保证复制省略”的语言功能的动机之一。 that paper中的示例:
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)
,directly的值。这就是这里的更改-复制初始化不是在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&);
这最终变得难以组合,但是可以工作。按照书面规定,它要求您的类型默认为可构造的,但也有一些解决方法。
答案 1 :(得分:3)
您在C ++ 17中观察guaranteed copy elision。
另一方面,即使编译器最终决定退出其调用,C ++ 11也要求存在一个副本或移动构造函数并且可以访问该构造函数。
答案 2 :(得分:2)
您需要删除对复制构造函数的显式删除,以便生成move构造函数(但也会是copy构造函数),或者像这样声明一个move构造函数:
Vector2(Vector2 &&) = default;
根据标准
如果类X的定义未明确声明移动 构造函数,当且仅当一个构造函数被隐式声明为默认构造函数 如果
— X没有用户声明的副本构造函数,
— X没有用户声明的副本分配运算符,
— X没有用户声明的移动分配运算符,并且
— X没有用户声明的析构函数。
用户声明是指用户提供(由用户定义),显式默认(=默认)或显式删除(=删除)
另一方面,在C ++ 17中,由于描述here的原因,省略了对复制/移动构造函数的调用:
在以下情况下,允许编译器,但即使复制/移动(自C ++ 11起)构造函数和析构函数也不需要省略类对象的复制和移动(自C ++ 11起)构造有明显的副作用。这些对象直接构造到存储中,否则会将它们复制/移动到其中。
...
(从C ++ 17开始)在对象的初始化中,当源对象是一个无名的临时对象,并且与目标对象具有相同的类类型(忽略cv限定)时。当无名临时变量是return语句的操作数时,复制省略的这种变体称为RVO,即“返回值优化”。 (直到C ++ 17)
返回值优化是强制性的,不再视为复制省略;见上文。
答案 3 :(得分:1)
您可以通过两个小的更改来修复它:
return {x, y};
的newVec()
。这样只会构造对象一次,不需要复制构造函数。const auto& v
而不是auto v
。这将具有相同的生存期(但您不能对其进行修改),并且不需要复制构造函数。