質問

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?

役に立ちましたか?

解決

(Note that as far as I see, std::array with std::get already solves your problem.)


The main issue is that you need your instance aw to be constexpr and of course you need to initialize it with some values:

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

Regarding the function at, you can write it as a normal function but simply specify it as constexpr:

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

Then, aw.at(0) can be used as a constant expression: Live Demo

The advantage of this is that you can use this function in both compile-time and runtime expressions, with static and dynamic indexing, respectively.


If you really want it to be templated, you can either write it as a non-member like std::get<N> or as a class member, but use a template parameter of type int (or size_t or similar). That simplifies its definition (and you can get rid of your compile_time_int class template):

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

Then, aw.at<0>() can be used as a constant expression: Live Demo

The advantage of the second method is that the index is guaranteed to be static, so we can use it in the function for static bound checking, which will not add any performance penalty. I don't know if this is possible with the first version.

他のヒント

Maybe just this:

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

Usage:

array_wrapper<10> x;
at<3>(x) = 42;
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top