我们可以通过函数中的值返回具有已删除/私有复制/移动构造函数的对象吗
Can we return objects having a deleted/private copy/move constructor by value from a function?
在C++03中,不可能按值返回具有私有未定义复制构造函数的类的对象:
struct A { A(int x) { ... } private: A(A const&); };
A f() {
return A(10); // error!
return 10; // error too!
}
我想知道,C++11中是否取消了这一限制,从而可以为没有用于复制或移动的构造函数的类编写具有类类型返回类型的函数?我记得允许函数的调用方使用新返回的对象可能很有用,但他们不能复制值并将其存储在某个地方。
以下是它如何工作
A f() {
return { 10 };
}
即使A
没有工作的复制或移动构造函数,也没有其他可以复制或移动A
的构造函数,这也能工作!
为了利用C++11的这一特性,构造函数(在本例中采用int
(必须是非显式的。
限制尚未解除。根据访问说明符,§12.8/32中有一条注释解释道:
无论是否会发生复制省略,都必须执行两阶段过载解析。如果不执行省略,它将确定要调用的构造函数,并且即使调用被省略,所选的构造函数也必须是可访问的。
截至删除的复制/移动构造函数§8.4.3/2规定
隐式或显式引用已删除函数而不是声明该函数的程序是格式错误的。[注意:这包括隐式或显式调用函数,并形成指向函数成员的指针或指针。它甚至适用于未潜在求值的表达式中的引用。如果函数重载,则只有在通过重载解析选择函数时才会引用它。--结束注]
不确定这个特定的情况,但我对这句话的理解是,如果在§12.8/32中的重载解决方案之后,选择了已删除的复制/移动构造函数,即使该操作被省略,也可能构成对该函数的引用,并且程序将是格式错误的。
上述代码在C++11中仍然格式不正确。但是您可以在A
中添加一个公共移动构造函数,这样它就合法了:
struct A
{
A(int x) {}
A(A&&);
private:
A(A const&);
};
A f() {
return A(10); // Ok!
}
我想知道,这个限制在C++11中取消了吗?
怎么可能呢?通过按值返回某个东西,从定义上讲,你就是在复制(或移动(它。虽然C++可以在某些情况下消除这种复制/移动,但它仍然是在按规范复制(或转移(。
我记得允许函数的调用方使用返回的对象可能很有用,但他们不能复制值并将其存储在某个地方。
是的。您去掉了复制构造函数/赋值,但允许移动值。std::unique_ptr
做到了这一点。
您可以按值返回unique_ptr
。但在这样做的过程中,你会返回一个"prvalue":一个正在被破坏的临时值。因此,如果您有g
这样的函数:
std::unique_ptr<SomeType> g() {...}
你可以这样做:
std::unique_ptr<SomeType> value = g();
但不是这个:
std::unique_ptr<SomeType> value1 = g();
std::unique_ptr<SomeType> value2 = g();
value1 = value 2;
但这是可能的:
std::unique_ptr<SomeType> value = g();
value = g();
第二行调用value
上的移动分配运算符。它将删除旧指针,并将新指针移动,使旧值为空。
通过这种方式,您可以确保任何unique_ptr
的内容只存储在一个地方。你不能阻止他们在多个地方引用它(通过指向unique_ptr
的指针或其他什么(,但内存中最多会有一个存储实际指针的位置。
删除复制和移动构造函数将创建一个不动的对象。创建它的地方就是它的价值所在,永远。移动可以让你拥有独特的所有权,但不会静止不动。
如果你真的想的话,你可能会破解一个代理来完成这项任务,并有一个转换构造函数来复制存储在代理中的值。
大致如下:
template<typename T>
struct ReturnProxy {
//This could be made private, provided appropriate frienship is granted
ReturnProxy(T* p_) : p(p_) { }
ReturnProxy(ReturnProxy&&) = default;
private:
//don't want these Proxies sticking around...
ReturnProxy(const ReturnProxy&) = delete;
void operator =(const ReturnProxy&) = delete;
void operator =(ReturnProxy&&) = delete;
struct SUPER_FRIENDS { typedef T GO; };
friend struct SUPER_FRIENDS::GO;
unique_ptr<T> p;
};
struct Object {
Object() : data(0) { }
//Pseudo-copy constructor
Object(ReturnProxy<Object>&& proxy)
: data(proxy.p ? proxy.p->data : throw "Don't get sneaky with me \glare")
{
//steals `proxy.p` so that there isn't a second copy of this object floating around
//shouldn't be necessary, but some men just want to watch the world burn.
unique_ptr<Object> thief(std::move(proxy.p));
}
private:
int data;
Object(const Object&) = delete;
void operator =(const Object&) = delete;
};
ReturnProxy<Object> func() {
return ReturnProxy(new Object);
}
int main() {
Object o(func());
}
不过,您可能可以在03中使用auto_ptr
s执行同样的操作。而且它显然不会阻止存储生成的Object
,尽管它确实将您的每个实例限制为一个副本。
- 当有分配器意识的容器被复制/移动时,反弹分配器是否被复制/移走
- std::元组分配和复制/移动异常保证
- std::async 如何工作:为什么它会调用这么多次复制/移动?
- 在临时将成员带出时省略复制/移动
- 是否可以避免在以下代码中复制/移动构造函数的需要?
- 默认复制/移动构造函数时 GDB 中的奇怪行为
- 返回 *&object 时是否允许复制/移动省略?
- 删除复制构造函数是否也会删除默认的复制/移动运算符?
- 用于删除复制/移动分配运算符的有效签名
- 复制 elision/RVO 会导致从同一对象复制/移动吗?
- 意外缺少隐式声明的复制/移动构造函数
- 是否可以传递具有捕获的不可复制(移动)值的 lambda
- CRTP 和复制/移动赋值/构造函数继承
- C++复制/移动构造函数和赋值运算符
- 覆盖复制/移动分配超载时,我是否需要删除当前的成员数据
- 为什么编译器在有模板构造函数时生成复制/移动构造函数
- C++:使用引用和值解压缩元组,而无需复制/移动太多
- 从C++中的基类继承复制/移动构造函数作为构造函数
- 初始化 std::数组而不复制/移动元素
- 是否允许复制/移动省略使使用已删除函数的程序格式正确?