为什么 std::unique_ptr 没有 const get 方法

Why does std::unique_ptr not have a const get method?

本文关键字:const get 没有 方法 std unique 为什么 ptr      更新时间:2023-10-16

我知道std::unique_ptr就是这样,可能不会改变以破坏向后兼容性,但我想知道是否有人有充分的理由为什么规范的作者没有用看起来像 const 变体重载get方法

,看起来像
const T* get() const;

遵循unique_ptrconst的意图。

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

更新

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

智能指针假装是原始指针。 如果你有原始指针的类成员,并在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 对象。

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

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

这里有一个很好的讨论,所有unique_ptr参数传递情况(in/out,const/non-const等):

  • 如何将unique_ptr参数传递给构造函数或函数?

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

指针的恒定性不同于指向的点性。

get是常量,它不会修改unique_ptr的状态。

它的恒定性不会影响内容的恒定性。

有一种聪明的指针可以传播恒定性的想法,但unique_ptr不是那种野兽。

std::experimental::propogate_const包裹一个类似指针的物体,并让const穿过它。

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

请注意,当我尝试让 const 像这样传播时,我发现我错了。 但这里可能不是这种情况。

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

我认为这种担忧是有道理的, 每个取消引用函数应该有 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 个版本用于取消引用)

因为就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操作。

你可能期待的是const承诺从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承诺变得肤浅。