7

我试图编写一个可以像这样使用的编译时 valarray:

constexpr array<double> a = { 1.0, 2.1, 3.2, 4.3, 5.4, 6.5 };

static_assert(a[0] == 1.0, "");
static_assert(a[3] == 4.3, "");

static_assert(a.size() == 6, "");

我设法通过以下实现来做到这一点,并且效果很好(使用 GCC 4.7):

#include <initializer_list>

template<typename T>
struct array
{
    private:

        const std::size_t _size;
        const T* _data;

    public:

        constexpr array(std::initializer_list<T> values):
            _size(values.size()),
            _data(values.begin())
        {}

        constexpr auto operator[](std::size_t n)
            -> T
        {
            return _data[n]
        }

        constexpr auto size() const
            -> std::size_t;
        {
            return _size;
        }
};

即使它对我来说效果很好,但我不确定它的行为std::initializer_list并且可能会使用一些未定义的行为。

constexpr对于std::initializer_list构造函数,begin即使size它不是严格意义上的 C++11 也很好,因为 N3471 最近被采用并达到了标准。

关于未定义的行为,我不确定底层数组是否std::initializer_list会存活,是否有办法让它比array's构造函数存活得更久。你怎么看?

编辑:我可能不清楚,但我并不真正关心实际的数组。我真正感兴趣的是std::initializer_list编译时的行为及其底层数组。

4

1 回答 1

7

您当前的代码不应根据当前的 C++11 规则进行编译。使用clang 3.2 编译时,出现以下错误:

source.cpp:33:28: error: constexpr variable 'a' must be initialized by a constant
expression 
constexpr array<double> a = { 1.0, 2.1, 3.2, 4.3, 5.4, 6.5 };
                        ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

这是因为std::initializer_lists ctors 和成员函数beginend没有被标记constexpr。然而,已经有一个改变这一点的提议。顺便说一句,libstdc++ 已经将这些标记为constexpr.

现在下一个问题是底层数组的生命周期std::initializer_list。这在 8.5.4p6 中有解释:

该数组与任何其他临时对象 (12.2) 具有相同的生命周期,除了从数组初始化 initializer_list 对象会延长数组的生命周期,就像将引用绑定到临时对象一样。

这意味着底层数组与values对象具有相同的生命周期,并且在array构造函数结束时退出。因此,_data指向过期的内存并且_data[n]是未定义的行为。

于 2013-04-10T23:17:56.543 回答