如何处理可以获取指向 const 或非 const 资源的指针的类?



我正在创建一个类型双关类View,它接受指向字节的指针并将其适应T数组。问题是非常量View可以从const byte*构造。我不想有单独的、不兼容的类型,比如ViewConstView。也许我可以有一个成员bool readonly,它在const byte*构造函数中设置并在非常量operator[]重载中被检查,导致它抛出。有没有更好的方法来解决这个问题?

using std::byte;
template <class T>
class View {
public:
typedef T __attribute__((may_alias)) value_type;
typedef value_type* pointer;
typedef const pointer const_pointer;
typedef value_type& reference;
typedef const reference const_reference;
View(byte* p)
: data { buffer }
{}
View(const byte* p)
: data { const_cast<byte*>(p) }
{}
reference operator[](int index) {
return reinterpret_cast<pointer>(data)[index];
}
const_reference operator[](int index) const {
return reinterpret_cast<const_pointer>(data)[index];
}
private:
byte* data;
};

我们实际上可以在编译时执行所有这些检查。您正在使用std::byte,所以我假设您至少在 C++17 上,这意味着这非常简单(我们可以用旧C++版本做很多这些技巧,但它涉及更多的模板技巧)

我们可以根据输入类型使用static_assert来启用或禁用功能。我们将使用is_const_v来检查我们的T类型是否const

template <class T>
class View {
public:
...
View(std::byte* p)
: data { p } {
static_assert(!std::is_const_v<T>);
}
View(const std::byte* p)
: data { const_cast<std::byte*>(p) } {
static_assert(std::is_const_v<T>);
}
reference operator[](int index) {
static_assert(!std::is_const_v<T>);
return reinterpret_cast<pointer>(data)[index];
}
const_reference operator[](int index) const {
return reinterpret_cast<const_pointer>(data)[index];
}
private:
std::byte* data;
};

static_assert就像assert一样,只不过它在代码生成时运行,而不是在运行时运行。所以我们定义了两个构造函数。一个人采取std::byte*,只有在T不是恒定时才存在。另一个采用const std::byte*,并且仅在T恒定时才存在。

同样,我们有两个重载operator[].第一个重载返回可变引用,但只有在T非常量时才可以使用。第二个返回一个const引用通常可以使用。我们不需要任何断言。(C++标准库到处都使用该习语:一个函数从 constthis指针返回常量引用,另一个函数返回可变引用,C++ 的重载规则可以处理它)

要使用

View<int> x { new std::byte[1] };
View<const int> y { const_cast<const std::byte*>(new std::byte[1]) };
// All fine
x[0] = 100;
std::cout << x[0] << std::endl;
std::cout << y[0] << std::endl;
// Fails at compile time
// y[0] = 100;
return 0;

此外,您需要在不久的某个时候彻底阅读三/五规则。你将指针作为参数,因此需要了解如何管理该资源。你要么需要(首选)使用智能指针而不是原始指针,要么如果你坚持使用原始指针,那么你需要编写自己的或删除析构函数,移动和复制构造函数,以及移动和复制赋值运算符。

最新更新