数组索引/指针算术在constexpr上下文中失败

问题描述 投票:0回答:2

我有一个矢量实现,试图将存储的值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是默认可构造的)]

c++ vector c++17 constexpr
2个回答
0
投票

对于SimpleVector,返回的指针指向数组的元素。编译器知道数组中包含多少个元素,并允许您将其作为constexpr函数的一部分来访问。

SimpleVector2::data返回一个指向单个double值的指针。您可以使用*取消引用它,也可以使用[0]访问第一个元素,但是如果尝试访问元素[1]您会遇到未定义行为,因为返回的指针指向单个值,而不是数组。在这种情况下,SimpleVector2的大小可能与double相同,因此您可以在运行时使用它。但是,在constexpr中不允许使用未定义行为,这会给您带来编译器错误。

一个可能的变化是,如果SimpleVector2::data将通过值,指针或引用返回Wrapper。添加'operator T`转换方法将使其使用变得透明。


0
投票

这是因为您将返回单个元素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);
© www.soinside.com 2019 - 2024. All rights reserved.