2014-10-10 36 views
2

我需要在一个C数组constexpr包装或用一些额外的构造器std::array(类似于std::vector构造函数):Constexpr包装对C数组的std ::矢量般的构造

template<class T, int N> 
struct wrapper { 
    T data[N]; 

    constexpr wrapper(int s);   // a 
    constexpr wrapper(int j, int k); // b 

    constexpr wrapper(...values...) // c 
    : data(...values...) {} 
}; 

我想得到了相同的行为std::vector构造,即:

constexpr wrapper<T,2> w(1); // calls a 
constexpr wrapper<T,2> w(1,2); // calls b 
constexpr wrapper<T,2> w{1}; // calls c 
constexpr wrapper<T,2> w{1,2}; // calls c 

注1:完美转发构造函数:

template<class... Args> 
constexpr wrapper(T&& t, Args&&... args) 
: data(std::forward<T>(t), std::forward<Args>(args)...) {} 

会胜过其他构造函数。

注2:T[N]/std::array<T,N>没有std::initializer_list<T>构造所以下面无法正常工作或:

constexpr wrapper(std::initializer_list<T> v) : data(std::move(v)) {} 

注3:如果值未在构造函数初始化列表中指定的包装类型不会工作,常量表达式。

回答

3

当然,只要使用index_sequence和委托构造函数:

constexpr wrapper(std::initializer_list<T> v) 
    : wrapper(std::make_index_sequence<N>{}, v) {} 
private: 
    template<std::size_t... Is> constexpr wrapper(
     std::index_sequence<Is...>, std::initializer_list<T> v) 
    : data{v.begin()[Is]...} {} 

这工作,因为每[support.initlist.access]initializer_list访问器是constexpr;其迭代器类型为E const*,因此我们可以在编译时访问其数组的任何已定义成员。

请注意,如果您提供了一个太短的braced-init-list(如v.size() < N),那么这将失败;在编译时如果用在一个常量表达式中,并且具有未定义的行为。这里有一个解决方案,默认构造T如果允许的话,否则抛出异常:

constexpr static T too_short(std::true_type) { return {}; } 
    T too_short(std::false_type) { 
    throw std::invalid_argument("braced-init-list too short"); } 
    template<std::size_t... Is> constexpr wrapper(
     std::index_sequence<Is...>, std::initializer_list<T> v) 
    : data{v.size() > Is ? v.begin()[Is] 
     : too_short(std::is_default_constructible<T>{})...} {} 

如果您想在一个太短的支撑,初始化列表称为初始化总是失败,只需更换右条件运算符的手分支与掷球表达式:

constexpr wrapper(std::initializer_list<T> v) 
    : wrapper(std::make_index_sequence<N>{}, v.size() == N ? v 
     : throw std::invalid_argument("braced-init-list incorrect length")) {} 
private: 
    template<std::size_t... Is> constexpr wrapper(
     std::index_sequence<Is...>, std::initializer_list<T> v) 
    : data{v.begin()[Is]...} {} 

它不大于在恒定表达成为可能的其他可移植到在编译时,该支撑-INIT-list是太短(或太长检测!),因为initializer_list隐藏了类型系统中的信息。

Full example