为什么 std::unique_ptr 没有 const get 方法?
Why does std::unique_ptr not have a const get method?
我知道 std::unique_ptr
是这样的,可能不会改变以破坏向后兼容性,但我想知道是否有人有充分的理由说明规范的作者没有超载get
方法的 const 变体看起来像
const T* get() const;
遵循 unique_ptr
的意图 const
。
我最好的猜测是它试图镜像指针并像 T* const
而不是典型的 class。作为后续问题,如果我想在我的 class 的 const 实例中以类似 const 的方式保存一个指针,我是否应该使用 std::unique_ptr
以外的其他东西来保存数据?
更新
在我的例子中,我想保护自己不滥用 class 本身的指针。我正在编写一个 const 移动构造函数 MyClass(const MyClass&& other)
,并通过 std::copy
将数据从新实例复制到其他实例。追踪错误花了很长时间,因为我认为由于 const 保护,副本一定是正确的。我想弄清楚除了提供 const getter 并在复制时在 class 中使用它之外,我可以做些什么来保护自己免受此影响。
通过 unique_ptr
授予对对象的只读访问权限毫无意义。您只在转移所有权时传递 unique_ptr
,为了在没有所有权转移的情况下访问对象,调用 up.get()
并将 const T*
传递给应该只读取的函数(或者如果指针永远不会 nullptr
,评估 *(up.get())
并传递 const T&
).
也是合理的
作为奖励,这允许您将该函数用于存储在堆栈中的对象、嵌入到另一个对象中的对象,或者使用 unique_ptr
以外的智能指针管理的对象。
此处对所有 unique_ptr 参数传递案例(in/out、const/non-const 等)进行了很好的讨论:
出于同样的原因,取消引用时 T*const
是 T&
,而不是 T const&
。
pointer 的常量不同于 pointed-to.
的 pointness
get
是const,不修改unique_ptr
.
的state
它的常量性不影响内容的常量性。
有传播常量的智能指针的想法,但 unique_ptr
不是那个野兽。
std::experimental::propogate_const
包装一个类似指针的对象并使 const
穿过它。
它或类似的东西可能会解决您的问题。
请注意,当我尝试像这样进行 const 传播时,我发现有一半的时间我发现我错了。但这里可能不是这样。
通常,以 const
方式处理 T*const
的正确方法是传递 T const&
(或可空变体 T const*
) .
智能指针伪装成原始指针。
如果你有 class 成员,它是原始指针并在 const
方法中使用它,你不能更新指针,但你可以修改指向的对象。
智能指针需要相同的行为。所以 std::unique_ptr::get
是一个 const
方法,但不会强制 return 指针指向 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
会 return 你所期待的。
基于以下评论:
只提供私有方法:
InnerClass& GetField() { return *uniquePtrToInnerClass; }
const InnerClass& GetField() const { return *uniquePtrToInnerClass; }
并在您的代码中使用它,您将在 const 方法中拥有内部 class 的 const 对象。
我认为这种担忧是有道理的,
每个解引用函数应该有 2 个版本,
e.g.
const T* get() const;
T* get();
enter code here
我知道提供 "T* get() const" 的目的是为了轻松替换现有的原始指针用法。
但是由于 uniq ptr 表示所有权,因此有人能够通过不可变(常量)引用修改对象拥有的某些东西是不正确的[假设修改对象完全拥有的东西与修改对象相同本身 - 如果这是一个对象而不是 ptr,则为真。
可能最好的选择是 std 提供另一个版本的 Uniq ptr,它保持上述习语(唯一的其他选择可能是从 uniq ptr 派生一个新的 class 并提供 2 个版本用于取消引用)
因为就unique_ptr
而言,获取内部原始指针引用是一个const
操作。调用 .get()
并检索 std::unique_ptr
的内部原始指针不会更改 std::unique_ptr
对象本身的内部状态。因此,库设计者似乎选择将其标记为 const
而没有关注如果他们只是 return 直接非 const
引用它的底层对象会发生什么。
事实上,如果你在一个对象中有一个std::unique_ptr
,并且你调用了那个对象的一个const
成员函数,你仍然可以调用非const
对象 内部 std::unique_ptr
的成员函数。例如:
struct A {
void Const() const { }
void nonConst() { }
};
struct B {
std::unique_ptr<A> a;
void go() const {
a->nonConst(); // OK
}
};
虽然您不能从其 const
成员函数之一对对象的内部状态变量执行非 const
操作,但没有规则说您不能执行非 const
对 其他 个对象的操作。
您可能期望的是 const
ness 承诺从 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
承诺变得有点肤浅。
我知道 std::unique_ptr
是这样的,可能不会改变以破坏向后兼容性,但我想知道是否有人有充分的理由说明规范的作者没有超载get
方法的 const 变体看起来像
const T* get() const;
遵循 unique_ptr
的意图 const
。
我最好的猜测是它试图镜像指针并像 T* const
而不是典型的 class。作为后续问题,如果我想在我的 class 的 const 实例中以类似 const 的方式保存一个指针,我是否应该使用 std::unique_ptr
以外的其他东西来保存数据?
更新
在我的例子中,我想保护自己不滥用 class 本身的指针。我正在编写一个 const 移动构造函数 MyClass(const MyClass&& other)
,并通过 std::copy
将数据从新实例复制到其他实例。追踪错误花了很长时间,因为我认为由于 const 保护,副本一定是正确的。我想弄清楚除了提供 const getter 并在复制时在 class 中使用它之外,我可以做些什么来保护自己免受此影响。
通过 unique_ptr
授予对对象的只读访问权限毫无意义。您只在转移所有权时传递 unique_ptr
,为了在没有所有权转移的情况下访问对象,调用 up.get()
并将 const T*
传递给应该只读取的函数(或者如果指针永远不会 nullptr
,评估 *(up.get())
并传递 const T&
).
作为奖励,这允许您将该函数用于存储在堆栈中的对象、嵌入到另一个对象中的对象,或者使用 unique_ptr
以外的智能指针管理的对象。
此处对所有 unique_ptr 参数传递案例(in/out、const/non-const 等)进行了很好的讨论:
出于同样的原因,取消引用时 T*const
是 T&
,而不是 T const&
。
pointer 的常量不同于 pointed-to.
的 pointnessget
是const,不修改unique_ptr
.
它的常量性不影响内容的常量性。
有传播常量的智能指针的想法,但 unique_ptr
不是那个野兽。
std::experimental::propogate_const
包装一个类似指针的对象并使 const
穿过它。
它或类似的东西可能会解决您的问题。
请注意,当我尝试像这样进行 const 传播时,我发现有一半的时间我发现我错了。但这里可能不是这样。
通常,以 const
方式处理 T*const
的正确方法是传递 T const&
(或可空变体 T const*
) .
智能指针伪装成原始指针。
如果你有 class 成员,它是原始指针并在 const
方法中使用它,你不能更新指针,但你可以修改指向的对象。
智能指针需要相同的行为。所以 std::unique_ptr::get
是一个 const
方法,但不会强制 return 指针指向 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
会 return 你所期待的。
基于以下评论:
只提供私有方法:
InnerClass& GetField() { return *uniquePtrToInnerClass; }
const InnerClass& GetField() const { return *uniquePtrToInnerClass; }
并在您的代码中使用它,您将在 const 方法中拥有内部 class 的 const 对象。
我认为这种担忧是有道理的, 每个解引用函数应该有 2 个版本,
e.g.
const T* get() const;
T* get();
enter code here
我知道提供 "T* get() const" 的目的是为了轻松替换现有的原始指针用法。
但是由于 uniq ptr 表示所有权,因此有人能够通过不可变(常量)引用修改对象拥有的某些东西是不正确的[假设修改对象完全拥有的东西与修改对象相同本身 - 如果这是一个对象而不是 ptr,则为真。
可能最好的选择是 std 提供另一个版本的 Uniq ptr,它保持上述习语(唯一的其他选择可能是从 uniq ptr 派生一个新的 class 并提供 2 个版本用于取消引用)
因为就unique_ptr
而言,获取内部原始指针引用是一个const
操作。调用 .get()
并检索 std::unique_ptr
的内部原始指针不会更改 std::unique_ptr
对象本身的内部状态。因此,库设计者似乎选择将其标记为 const
而没有关注如果他们只是 return 直接非 const
引用它的底层对象会发生什么。
事实上,如果你在一个对象中有一个std::unique_ptr
,并且你调用了那个对象的一个const
成员函数,你仍然可以调用非const
对象 内部 std::unique_ptr
的成员函数。例如:
struct A {
void Const() const { }
void nonConst() { }
};
struct B {
std::unique_ptr<A> a;
void go() const {
a->nonConst(); // OK
}
};
虽然您不能从其 const
成员函数之一对对象的内部状态变量执行非 const
操作,但没有规则说您不能执行非 const
对 其他 个对象的操作。
您可能期望的是 const
ness 承诺从 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
承诺变得有点肤浅。