移动涉及 const unique_ptr 的构造函数

Move constructor involving const unique_ptr

在下面的代码中,我将 p 设置为 const,因为它在 Foo 的生命周期内永远不会指向任何其他 int。这不会编译,因为 unique_ptr 的复制构造函数被调用,显然被删除了。除了使 p 非常量之外还有什么解决方案吗?谢谢

#include <memory>

using namespace std;

class Foo 
{
public:
  //x is a large struct in reality
  Foo(const int* const x) : p(x) {};
  Foo(Foo&& foo) : p(std::move(foo.p)) {};
private:
  const unique_ptr<int> p;
};

如果要防止所有权转移,可以使用const std::unique_ptr<T>。这个用处不大。

如果你想防止修改它持有的对象,你可以使用std::unique_ptr<const T>.

使用 std::unique_ptr 的概念表示对象的唯一所有权。你想要实现的是让 Foo class 拥有一个对象(由 std::unique_ptr 表示) 使其可移动(你的移动构造函数)这造成矛盾。我会坚持使用 std::unique_ptr 或使用 std::shared_ptr.

共享它

您可能想阅读以下内容: Smart Pointers: Or who owns you baby?

你的移动构造函数的语义是矛盾的。

您已经声明了一个 const std::unique_ptr 它将(唯一地)拥有它被初始化的值。 但是您已经声明了一个移动构造函数,它应该在构造时将该值移动到另一个对象中。

那么你认为 'temporary' 中的 std::unique_ptr 应该怎么处理?

如果您想要release()编辑它,您就违反了它的const性。 如果你想让它保留它的价值,你就违反了 std::unique 的约束,只需要一个这样的对象就可以拥有任何给定的对象。 将死。

这个问题揭示了 C++ 语言的一个微妙的局限性。它需要 move 语义才能将复制到 的内容作为有效对象。

'destructive move' 有几个非常合理的建议,实际上可以更好地反映 move 的大多数用途 - 从那里取值 'invalidating' 那里有什么.

Google 他们。我没有做过文献调查,所以不想推荐。

您的替代方案是删除 const 或强制转换。 我强烈建议删除它。您可以确保 class 的语义确保适当的 const-ness 没有影响,也没有 'ugly suspect' const_cast

#include <iostream>
#include <memory>

class Foo 
{
public:
  Foo(const int x) : p(new int(x)) {};
  Foo(Foo&& foo) :
    p(std::move(foo.p)) {

    };

    int get(void)const{
        return *(this->p);
    }

private:
     std::unique_ptr<int> p;
};

Foo getMove(){
    return Foo(88);
}

int main(){

    Foo bar(getMove());    
    std::cout<<bar.get()<<std::endl;

    return EXIT_SUCCESS;
}

要了解为什么您的代码无法编译,请反映您是如何声明 Foo class 以及 move semantics 通常是如何实现的。

声明一个 const unique_ptr<T> p,你的意思是 p 本身永远不会被修改,但是你仍然可以修改指向的对象,因为 T 不是常量。

但是 move 基于相反的假设。此功能使用允许从对象窃取资源并使它们处于空状态(如果空状态有意义)的想法。如果有用,请将 move 视为移动对象的一种 'destructive' 副本。

std::move(foo.p),基本上就是窃取了foo.p指向的资源,让其处于安全状态,也就是说把foo.p赋值给NULL。但是foo.p被声明为const,所以操作是不允许的。

请考虑在您的情况下您不需要将 p 声明为 const unique_ptr<int>。只需将其声明为 unique_ptr<int> 并确保成员函数声明为 const 而非成员函数将其视为 const unique_ptr<int> p& 参数。通过这种方式,您可以确保 p 永远不会随着对象的生命周期而改变(移动操作除外)。

因为unique_ptr只有移动构造函数,也就是说p的初始化参数不能是const,而p是const。我想你想要的是声明

unique_ptr p;

而不是

const unique_ptr p;

class Foo {
  public:
    // x is a large struct in reality
    Foo(const int* const x) : p(x) {};
    Foo(Foo&& foo) : p(std::move(foo.p)) {};
  private:
    const unique_ptr<int> p;
};