1

Given a template whose non-type parameter determines the size of a non-const int array member, how can I access the array elements by an integral index at compile time? I want the access to be done via the class template’s getter method at.

I figured since class templates must be instantiated before runtime, I can pass another non-type class template’s enum member value to the prior class’s at method to ensure the index argument is a compile-time constant.

I left the class template deliberate_error undefined to see if its arguments are computed at compile time and to view the compile-time results in the error messages.

template <unsigned int N>
struct compile_time_int {
    enum {num = N};
};

template <unsigned int N>
struct array_wrapper {

    int arr[N];

    template <unsigned int Ind>
    constexpr int const& at(compile_time_int<Ind> const& index) const {
        return arr[index.num];
    }
};

template <unsigned int> struct deliberate_error;

int main() {
    compile_time_int<2> cti;
    array_wrapper<3> aw;
    aw.at(cti);
    deliberate_error<cti.num> my_error1;
    deliberate_error<aw.at(cti)> my_error2;
}

aw.at(cti); doesn’t give an error, so I thought that if I passed the same expression to deliberate_error instance my_error2, the compiler will display the value of arr[2] in the error message.

my_error1 causes g++ error: aggregate 'deliberate_error<2u> my_error1' has incomplete type and cannot be defined,

showing cti’s wrapped integral value 2. So, I thought if I passed the same cti to object aw's getter, and then pass the result to my_error2, I can get arr[2] in the error message. But instead, it prints:

error: the value of 'aw' is not usable in a constant expression
note: 'aw' was not declared 'constexpr'
note: in template argument for type 'unsigned int'
error: invalid type in declaration before ';'

So, I tried prepending constexpr to aw’s declaration, but that gives even more undesirable errors. What’s wrong here, and how can I fix it?

4

2 回答 2

2

也许只是这样:

template <unsigned int N>
struct array_wrapper
{
    int arr[N];
};

template <unsigned int I, unsigned int N>
constexpr int & at(array_wrapper<N> & a)
{
    static_assert(I < N, "static array index out of bounds");
    return a.arr[I];
}

// add a "const" overload, too

用法:

array_wrapper<10> x;
at<3>(x) = 42;
于 2013-12-21T23:05:49.627 回答
2

(请注意,据我所知,std::arraywithstd::get已经解决了您的问题。)


主要问题是您需要您的实例awconstexpr当然您需要使用一些值对其进行初始化:

constexpr array_wrapper<3> aw = { 1, 2, 3 };

关于函数at,您可以将其编写为普通函数,但只需将其指定为constexpr

constexpr int const& at(int i) const {
    return arr[i];
}

然后,aw.at(0)可以用作常量表达式:Live Demo

这样做的好处是您可以在编译时和运行时表达式中使用此函数,分别具有静态和动态索引。


如果您真的希望它被模板化,您可以将它写为非成员std::get<N>或类成员,但使用类型int(或size_t或类似)的模板参数。这简化了它的定义(你可以摆脱你的compile_time_int类模板):

template<int Index>
constexpr int const& at() const {
    return arr[Index];
}

然后,aw.at<0>()可以用作常量表达式:Live Demo

第二种方法的好处是保证索引是静态的,所以我们可以在函数中使用它进行静态边界检查,不会增加任何性能损失。我不知道第一个版本是否可行。

于 2013-12-21T23:22:39.237 回答