【问题标题】:Why does std::unique_ptr not have a const get method?为什么 std::unique_ptr 没有 const get 方法?
【发布时间】:2017-10-18 13:54:41
【问题描述】:

我知道std::unique_ptr 是这样的,并且可能不会更改以破坏向后兼容性,但我想知道是否有人有充分的理由说明规范的编写者没有重载get 方法带有一个看起来像

的 const 变体
const T* get() const;

遵循unique_ptr 成为const 的意图。

我最好的猜测是它试图镜像指针并像T* const 而不是典型的类。作为后续问题,如果我想在我的类的 const 实例中以类似 const 的方式保存指针,我是否应该使用 std::unique_ptr 以外的其他东西来保存数据?

更新

就我而言,我想保护自己不误用类本身中的指针。我正在编写一个 const move 构造函数MyClass(const MyClass&& other),并通过std::copy 将数据从新实例复制到其他实例中。由于 const 保护,我假设副本必须是正确的,因此花了很长时间才找到错误。我试图弄清楚我可以做些什么来保护自己免受这种情况的影响,除了提供一个 const getter 并在进行复制时在类中使用它。

【问题讨论】:

  • 关于后一个问题,std::unique_ptr<YourClass const>不会做你所寻求的吗?还是我误解了问题的那一部分。
  • 在我的例子中,我假设我只是包装一些数据,比如结构,所以它只是一个 char[] 和一个 int 来表示长度。我想传递它并让消费者操纵字节,除非他们有一个 const 实例。即使我为数据提供 const 和非 const getter,我也想确保我的内部方法不会与传递给我的类方法的 const 实例上的数据混淆
  • 我不明白你的更新。这似乎是一个 XY 问题,或者完全不同的问题。为什么你想要一个 const move 构造函数?什么是“常量保护”?
  • 您的 const move 构造函数没有任何意义,如果我理解正确,您只是实现了一个复制构造函数。如果移动构造函数在你的类中没有意义,那很好,不要创建一个,不需要破解任何东西......

标签: c++ c++11 constants smart-pointers


【解决方案1】:

智能指针伪装成原始指针。 如果您有原始指针的类成员并在 const 方法中使用它,则您无法更新指针,但您可以修改指向的对象。 智能指针需要相同的行为。所以std::unique_ptr::get 是一个const 方法,但不会强制返回指向const 对象的指针。

还请注意,您可以拥有指向 const 对象的指针。

MyClass *pointerToObject
std::unique_ptr<MyClass> smartPointerToObject;

// but you can have also a case
const MyClass *pointerToConstObject
std::unique_ptr<const MyClass> smartPointerToConstObject;

在最后一种情况下,std::unique_ptr::get 将返回您期望的内容。


基于以下评论:

只提供私有方法:

InnerClass& GetField() { return *uniquePtrToInnerClass; }
const InnerClass& GetField() const { return *uniquePtrToInnerClass; }

并在您的代码中使用它,您将在 const 方法中拥有内部类的 const 对象。

【讨论】:

  • 我希望内容是可变的,如果实例是可变的和不可变的,那么我不知道何时实例化类或存储数据是否将其存储为一个 const 或非 const 版本。
  • std::unique_ptr&lt;const MyClass&gt; 不能满足您的需求?你在评论里写了这么复杂的句子,我无法理解。
  • 它不能满足需求。考虑有OuterInnerOuter 包含unique_ptr&lt;Inner&gt;。 Outer 有时需要操作 Inner 实例,但在 const 方法中操作 Inner 会违背 Outer 的功能。
【解决方案2】:

通过unique_ptr 授予对对象的只读访问权限是没有意义的。你只在转移所有权时传递unique_ptr,为了在没有所有权转移的情况下访问对象,调用up.get()并将const T*传递给只应该读取的函数(或者如果指针永远不会nullptr ,评估*(up.get())并通过const T&amp;也是合理的)。

作为奖励,这允许您将该函数用于存储在堆栈中、嵌入到另一个对象中或使用unique_ptr 以外的智能指针管理的对象。

这里对所有 unique_ptr 参数传递情况(输入/输出、常量/非常量等)进行了很好的讨论:

【讨论】:

  • 我要解决的问题不是传递 unique_ptr,而是在访问 const 实例时保护我免受课堂内的伤害。我可以为每个类中的每个智能指针添加一个 const get 以始终​​保护自己,但这是很多额外的代码。
  • 我猜他的意思是你可以从const 成员函数中改变unique_ptr 成员的内部结构,即使该成员函数被标记为const。如果不是unique_ptr,你就不能这样做(例如,如果你存储了一个原始指针而不是unique_ptr
  • @bobobobo:如果你有一个原始指针,你仍然可以改变指向的对象。指针类型将是 T* const 而不是 const T*。您无法替换指针(但get() 不建议替换指针)
【解决方案3】:

出于同样的原因,T*const 在取消引用时是 T&amp;,而不是 T const&amp;

pointer 的常量与 pointed-to 的pointness 不同。

get是const,不会修改unique_ptrstate

它的常量不影响内容的常量。

存在传播 const 的智能指针的想法,但 unique_ptr 不是那种野兽。

std::experimental::propogate_const 包装一个类似指针的对象并让const 穿过它。

它或类似的东西可能会解决您的问题。

请注意,当我尝试像这样进行 const 传播时,我发现有一半时间我发现我错了。但这里可能不是这样。

一般来说,以const 方式处理T*const 内容的正确方法是传递T const&amp;(或可空变体T const*)。

【讨论】:

  • std::experimental::propagate_const&lt;std::unique_ptr&lt;T&gt;&gt; ?
  • 你只是在重复这个问题,它说“我最好的猜测是它试图镜像指针并表现得像 T* const 而不是典型的类。”
  • propogate_const 看起来有些有希望,但在我的情况下,使用它会导致奇怪的模式,即在 const 方法中访问它们时,总是在此调用中包装对 unique_ptrs 的调用。这有点接近我想要的,但在每个使用 unique_ptr 的类中使用仍然相当冗长
  • @quittle 这个“deep const”不应该是“无处不在的东西”吗?在任何情况下你都可以写一个别名——template&lt;class T&gt; using flat_ptr = std::experimental::propotate_const&lt;std::unique_ptr&lt;T&gt;&gt;。此类仅在包含逻辑 const 指针的类中使用才有意义; unique_ptr 的其他用途不应该使用它。
【解决方案4】:

我认为这种担忧是有道理的, 每个解引用函数应该有 2 个版本,

e.g.
    const T* get() const;
    T* get();
    enter code here

我知道提供“T* get() const”的目的是为了方便替换现有的原始指针用法。

但是由于 uniq ptr 表示所有权,因此有人能够通过不可变(const)引用修改对象所拥有的某些东西是不正确的[假设修改对象完全拥有的东西与修改对象本身相同 -如果这是一个对象而不是一个ptr,这是正确的]。

最好的选择可能是 std 提供另一个版本的 uniq ptr 以保持上述成语(只有其他选择可能是从 uniq ptr 派生一个新类并提供 2 个版本用于取消引用)

【讨论】:

    【解决方案5】:

    因为就unique_ptr 而言,获取内部原始指针引用是const 操作。调用.get() 并检索std::unique_ptr 的内部原始指针不会改变std::unique_ptr 对象本身的内部状态。因此,图书馆设计者似乎选择将其标记为const,而不注意如果他们只返回一个直接的非const 引用,底层对象会发生什么。

    事实上,如果你在一个对象中有一个std::unique_ptr,并且你调用该对象的一个​​const成员函数,你仍然可以在内部std::unique_ptr上调用非const成员函数在该对象内。例如:

    struct A {
        void Const() const { }
        void nonConst() { }
    };
    
    struct B {
        std::unique_ptr<A> a;
        void go() const {
            a->nonConst(); // OK
        }
    };
    

    虽然您不能从对象的const 成员函数之一对对象的内部状态变量执行非const 操作,但没有规则说您不能对 执行非const 操作其他对象。

    您可能期望constness 承诺从unique_ptr 继承,也适用于访问它内部指向的内容,因此您希望unique_ptr 写成这样:

    template <typename T>
    class cunique_ptr {
        T* ptr;
    public:
        cunique_ptr() {
            ptr = new T;
        }
        ~cunique_ptr() {
            delete ptr;
        }
    
        // You can only get a non-const pointer to the internals from a non-const object
        T* get() { return ptr; }
    
        // The const member function carries over the const promise to access to its internals
        const T* get() const { return ptr; }
    };
    
    void test() {
        cunique_ptr<A> a;
        a.get()->nonConst();
    
        const cunique_ptr<A> ca;
        //ca.get()->nonConst(); //X fails: cannot call non-const member functions from const object
        ca.get()->Const();
    }
    

    然而,图书馆设计者似乎反对这种类型的保护,并让 const 承诺有点肤浅。

    【讨论】:

      猜你喜欢
      • 2012-06-03
      • 1970-01-01
      • 2012-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多