2017-09-01 12 views
0
#include <array> 
#include <tuple> 

typedef std::tuple<const int> TupleType; 

constexpr std::array<const int, 2> a = {1, 2}; 

constexpr void foo() 
{ 
    for (std::size_t i = 0; i < a.size(); ++i) 
    { 
     const int j = i; 
     typedef std::tuple_element<j, TupleType> T; 
    } 
} 

コードは--std = CとGCC-7.2でコンパイルすることができない次のコンパイルエラーで++ 17コンパイル時間型生成

error: the value of 'j' is not usable in a constant expression 
note: in template argument for type 'long unsigned int' 

関数(および対応するループ)がコンパイル時に評価されると仮定すると(これはC++ 14から始まるループで実行可能です)、なぜこのコードはconstと宣言されていなくても、実際にはconstexprとなります。その値はすべてコンパイル時にもわかっています。

このコードがそのアイデアによって無効であるかどうかを教えてください。コンパイラの制限がありますか?または次のどれですか?

答えて

2

Could you please clarify whether this code is invalid by its very idea?

それはある - あなたは定数式として可変とステートフル反復変数を使用しようとしています。 定数式の全体の概念は、不変性を中心にしています。コンパイル中にループが実行されるかどうかは関係ありません。あなたが実際にここに何をすべき

は、次のスニペットのコードを生成です:

j定数式のプレースホルダです
{ 
    typedef std::tuple_element<j, TupleType> T; 
    // ... 
} 

template <typename F, typename... Ts> 
constexpr void for_each_arg(F&& f, Ts&&... xs) 
{ 
    (f(std::forward<Ts>(xs)), ...); 
} 

constexpr void foo() 
{ 
    for_each_arg([](auto c) 
    { 
     typedef std::tuple_element<c, TupleType> T; 
    }, 
    std::integral_constant<int, 1>{}, 
    std::integral_constant<int, 2>{}); 
} 

live example on wandbox

for_each_argよりも高いレベルの抽象化を簡単に(例えば数字のコンパイル時間範囲を反復処理、または変換constexpr配列を提供することができること:ここでそれを行うための可能な方法がありますintegral_constantのシーケンスに置き換え、その代わりに繰り返します)

0

すべてconstexpr実行時に関数を評価できる必要があります。

constexprは「コンパイル時に実行する必要がある」という意味ではなく、「コンパイル時に実行される可能性がある」という意味です。

constexpr forのループで、各繰り返しでインデックスをconstexprにすることができなかった理由はありません。しかし、C++にはそれがありません。

constexpr ifは欲しいものと似ています。

それが得られるまで、あなた自身で作成する必要があります。

template<std::size_t...Is> 
constexpr auto index_over(std::index_sequence<Is...>){ 
    return [](auto&& f){ 
    return decltype(f)(f)(std::integral_constant<std::size_t, Is >{}...); 
    }; 
} 
template<std::size_t N> 
constexpr auto index_upto(std::integral_constant<std::size_t,N> ={}){ 
    return index_over(std::make_index_sequence<N>{}); 
} 
template<class F> 
constexpr auto foreacher(F&&f){ 
    return [f](auto&&...args){ 
    ((void)(f(decltype(args)(args)), ...); 
    }; 
} 

constexpr void foo() 
{ 
    index_upto<a.size()>()(foreacher([](auto I){ 
    typedef std::tuple_element<I, TupleType> T; 
    }); 
} 

(それはconstexprのラムダを有するのでほとんど17)C++ 17で未コンパイルexampkeあります。

1

コンパイラが正しいです。 iおよびjであり、constexprではありません。あなた自身のために見て:あなたはj constexprのをマークしようとした場合

// v--- not constexpr 
for (std::size_t i = 0; i < a.size(); ++i) 
    { 
     // Not constexpr either 
     const int j = i; 
     typedef std::tuple_element<j, TupleType> T; 
    } 

、あなたはiではないので、それはそうではないことを参照してくださいね。

constexprを宣言しようとすると、constexpr変数がconstexpr変数と同じ規則の対象になることがわかります。それらを変更することはできません。

どのように番号をループして型を生成できますか?

あなたは、インデックス配列とパックの拡張を使用することができます。

template<typename T, T... S, typename F> 
void for_sequence(std::integer_sequence<S...>, F f) 
{ 
    using unpack = int[]; 
    (void) unpack{(f(std::integral_constant<T, S>{}), void(), 0)..., 0}; 
} 

constexpr void foo() 
{ 
    for_sequence(std::make_index_sequence<a.size()>{}, [](auto i) 
    { 
     typedef std::tuple_element<i, TupleType> T; 
    }); 
} 
関連する問題