2014-10-10 37 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