gpt4 book ai didi

c++ - 如何编写 const 传播指针类型包装器?

转载 作者:行者123 更新时间:2023-12-03 07:00:37 25 4
gpt4 key购买 nike

为了更好地理解 C++ 类型系统,我努力编写了一个指针包装类,它传播类似于 std::experimental::propagate_const 的常量:

template <typename Pointee> class Ptr {
public:
Ptr() = delete;

explicit Ptr(Pointee *);

Ptr(const Ptr<Pointee> &) = delete;
Ptr(Ptr<Pointee> &&);

Ptr<Pointee> &operator=(const Ptr<Pointee> &) = delete;
Ptr<Pointee> &operator=(Ptr<Pointee> &&);

~Ptr() = default;

const Pointee *operator->() const;
Pointee *operator->();
const Pointee &operator*() const;
Pointee &operator*();

private:
Pointee *mPtr;
};

包装器旨在提供接近原始指针的行为,同时还强制执行一种“深度”const 正确性并防止无意的别名。

为此删除了拷贝构造函数和拷贝赋值运算符:

  1. 防止指向对象的无意别名从 Ptr 复制。
  2. 防止对 const 的非常量访问通过将 const Ptr 复制到非常量 Ptr 来指向对象。

但是,上述设计有两个不幸的后果。

  1. const Ptr 不能移动到 const 或非常量 Ptr 中。当 C++17 的强制 RVO 不适用时,这意味着不能从函数返回 const Ptr 对象。
  2. 由于 C++17 的强制复制/移动省略,在某些情况下,即使不存在这样的可行构造函数,也可以从 const Ptr 构造非常量 Ptr。例如,下面的代码将编译得很好(为了演示而忽略内存泄漏/原始新):
const Ptr<int> allocateImmutableInt(int val) { return Ptr<int>(new int(val)); }


void foo() {
Ptr<int> immutableInt = allocateImmutableInt(0); // Initializes non-const Ptr from const Ptr
*immutableInt = 100; // Oops, changed value of 'immutable' object
}

第一个问题可以通过引入一个接受 const 右值引用的 move ctor 来部分解决(尽管这感觉有些奇怪和不符合习惯):

  Ptr(const Ptr<Pointee> &&);

然而,这实际上加剧了第二个问题。现在,即使没有强制移动/复制省略,也可以将 const Ptr 移动构造为非常量 Ptr。据我所知,为了解决这个问题,我们需要一个所谓的“const 构造函数”,即只能调用以生成 const 对象的构造函数:

  Ptr(const Ptr<Pointee>&&) const;

即使 c++ 支持这样的构造函数,第二个问题仍然存在,因为 c++17 在决定是否可以在初始化对象时应用强制移动/复制省略时特别忽略了 cv 限定和构造函数的可行性。目前似乎没有办法要求 C++ 在将强制复制/移动省略应用于对象初始化之前检查复制/移动是否可行。

据我所知,std::experimental::propagate_const 也存在同样的问题。我想知道我是否遇到了 C++ 的基本限制,或者我是否错误地设计了 Ptr 包装器?我知道这些问题可能可以通过创建两种类型来消除,一种是用于非 const 访问的 Ptr,另一种是用于仅 const 访问的 ConstPtr。然而,这首先违背了创建常量传播包装器的目的。

也许我刚刚偶然发现了迭代器类型和 const_iterator 类型同时存在的原因。

最佳答案

您正在查看实际上并不存在的问题。

  1. To prevent non-const access of a constpointed-to object by copying a const Ptr into a non-const Ptr.

这不应该是一个目标,因为它违背了传播 const 的想法。 .

传播有两个方面const .首先,当指针为const时-合格,对象也是const -合格的。这方面你已经涵盖了。第二,当指针不是 const -qualified,对象使用其自然资格。也就是说,如果您可以复制 const Ptr变成非 const Ptr ,然后该更改传播到对象,可能使对象也非 const .这是期望的传播,而不是要阻止的东西。

请记住 const 的一个主要用例传播:类成员。传播 const对于成员指针,通过指向数据 const 来帮助确保常量正确性在 const - 合格的成员函数。您想象的问题不适用于此用例。不要让情况变得比它需要的更复杂。

I am aware that these issues can likely be eliminated by creating two types, a Ptr for non-const access and ConstPtr for const-only access.

这不是必须的。如果对象应该保留 const即使指针不是 const , 那么类型应该是 Ptr<const T>而不是 Ptr<T> .例如,您的“不可变整数”应该看起来更像以下内容。

Ptr<const int> allocateImmutableInt(int val) { return Ptr<const int>(new int(val)); }
^^^^^ ^^^^^

const已移动以符合 int 的资格, 使得 int无论const如何都是不可变的-资格 Ptr .

此外,您可能会注意到 new int(val)返回 int*隐式转换为 const int*为您的构造函数。您可能希望为 Ptr 复制此隐式转换.像 Ptr(Ptr<std::remove_const<Pointee>> &&) 这样的构造函数只要它仅在 Pointee 时定义,就可以解决问题是const -限定(以避免与常规移动构造函数发生冲突)。

关于c++ - 如何编写 const 传播指针类型包装器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64196584/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com