我有一个矢量实现,试图将存储的值T
放在包装器中。
包装程序与T
具有相同的内存布局,因此在T*
上执行数组索引/指针算术似乎在运行时可以正常工作。但是,当尝试在constexpr
上下文中执行相同操作时,[gcc,clang,msvc]都会拒绝该代码。另请注意,data()[0]
在所有情况下均有效,但是data()[1]
,data()[2]
等均失败。
会是这种情况吗?为什么编译器决定对SimpleVector
进行指针算术很好,但对SimplerVector2
则不做指针算术?此外,运行时指针算术(似乎起作用)是否可以安全使用?
#include <array>
#include <cassert>
struct Wrapper
{
double b;
};
struct SimpleVector
{
std::array<double, 100> values;
constexpr const double* data() const { return &values[0]; }
};
struct SimpleVector2
{
std::array<Wrapper, 100> values;
constexpr const double* data() const { return &values[0].b; }
};
int main()
{
{
constexpr SimpleVector my_vec{1, 2, 3, 4, 5, 6};
assert(my_vec.data()[0] == 1); // OK
assert(my_vec.data()[1] == 2); // OK
assert(my_vec.data()[2] == 3); // OK
static_assert(my_vec.data()[0] == 1); // OK
static_assert(my_vec.data()[1] == 2); // OK
static_assert(my_vec.data()[2] == 3); // OK
}
{
constexpr SimpleVector2 my_vec{1, 2, 3, 4, 5, 6};
assert(my_vec.data()[0] == 1); // OK
assert(my_vec.data()[1] == 2); // OK
assert(my_vec.data()[2] == 3); // OK
// OK!!
static_assert(my_vec.data()[0] == 1);
// FAILS! read of dereferenced one-past-the-end pointer
// is not allowed in a constant expression
static_assert(my_vec.data()[1] == 2);
// FAILS! cannot refer to element 2 of non-array object
// in a constant expression
static_assert(my_vec.data()[2] == 3);
}
return 0;
}
实时代码:https://godbolt.org/z/u_Mgtg
((注意:我尝试包装这些值的原因是可以执行optional_storage,因此不需要T
是默认可构造的)]
对于SimpleVector
,返回的指针指向数组的元素。编译器知道数组中包含多少个元素,并允许您将其作为constexpr函数的一部分来访问。
SimpleVector2::data
返回一个指向单个double值的指针。您可以使用*
取消引用它,也可以使用[0]
访问第一个元素,但是如果尝试访问元素[1]
您会遇到未定义行为,因为返回的指针指向单个值,而不是数组。在这种情况下,SimpleVector2
的大小可能与double
相同,因此您可以在运行时使用它。但是,在constexpr
中不允许使用未定义行为,这会给您带来编译器错误。
一个可能的变化是,如果SimpleVector2::data
将通过值,指针或引用返回Wrapper
。添加'operator T`转换方法将使其使用变得透明。
这是因为您将返回单个元素b
,而不是指向数组的指针。
如果您这样操作,它将起作用:
struct SimpleVector2
{
std::array<Wrapper, 100> values;
constexpr const Wrapper* data() const { return &values[0]; }
};
//...
static_assert(my_vec.data()[0].b == 1);
static_assert(my_vec.data()[1].b == 2);
static_assert(my_vec.data()[2].b == 3);