按索引编译时对类模板成员的访问

时间:2013-12-21 22:59:52

标签: c++ arrays templates template-meta-programming compile-time-constant

给定一个模板,其非类型参数确定非const int数组成员的大小,如何在编译时通过整数索引访问数组元素?我希望通过类模板的getter方法at完成访问。

我认为,因为类模板必须在运行时之前实例化,所以我可以将另一个非类型类模板的enum成员值传递给先前类的at方法,以确保index参数是编译时常量。

我将类模板deliberate_error置于未定义状态,以查看其参数是否在编译时计算,并在错误消息中查看编译时结果。

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);没有给出错误,所以我认为如果我将同一个表达式传递给deliberate_error实例my_error2,编译器将显示arr[2]的值在错误消息中。

my_error1会导致g ++ error: aggregate 'deliberate_error<2u> my_error1' has incomplete type and cannot be defined

显示cti的包装整数值2。所以,我想如果我将相同的cti传递给对象aw的getter,然后将结果传递给my_error2,我可以在错误消息中获得arr[2]。但相反,它打印:

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 ';'

所以,我尝试将constexpr添加到aw的声明中,但这会产生更多不良错误。这里有什么问题,我该如何解决?

2 个答案:

答案 0 :(得分: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;

答案 1 :(得分:2)

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


主要问题是您需要将实例aw设为constexpr,当然您需要使用一些值对其进行初始化:

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的模板参数(或{{ 1}}或类似的)。这简化了它的定义(你可以摆脱你的size_t类模板):

compile_time_int

然后,template<int Index> constexpr int const& at() const { return arr[Index]; } 可用作常量表达式:Live Demo

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