2016-04-29 13 views
8

私はvariadicテンプレートを作成しています。このクラスのvariadicテンプレートのパラメータパックの値をstatic_assertするにはどうすればよいですか?

template<typename T, T ... Numbers> 
class Sequence final { 

    // Unpack parameter pack into a constexpr array 
    constexpr static T count = sizeof...(Numbers);   
    constexpr static T numbers[count] = { Numbers... }; 

    // ... 
} 

インスタンスは次のようにインスタンス化することができます:

Sequence<uint32_t, 1, 2, 3, 42, 25> seq; 

私はstatic_assertそのnumbersパラメータを使用して、コンパイル時に確認したいのですが
のは、私はこのような何かを持っているとしましょうパックには特定の番号のみが含まれています。この例では、0または1だけを許可したいとします。

だから私は、ような何かしたいと思います:

for (size_t i = 0; i < count; i++) { 
    static_assert(numbers[i] == 1 || numbers[i] == 0, "Only ones and zeroes are allowed."); 
} 

しかし、明らかに、static_assertforループでは動作しません。私は確かにこれのための構文のいくつかの並べ替えが必要ですが、私はそれを把握することができていないと確信しています。

私はC++ 11コンパイラ(またはおそらくC++ 11で実行できない場合はC++ 14コンパイラ)でコンパイルするものを使用したいと考えています。

+2

C++では、静的なアサート(((Numbers == 0 || Numbers == 1)&& ... ... && true)); '([Demo](http://melpon.org/wandbox/) permlink/IbYXFUPVkK3RM35P)) –

+0

ブーストを使用している場合は、 'BOOST_PP_COMMA_IF'と' BOOST_PP_SEQ_FOR_EACH_I'がそのトリックを行うことができると思います。 –

+0

@KerrekSB '&& true'もオプションです。 –

答えて

17

私は@Columbo's bool_pack trickにスローされます。

template<bool...> struct bool_pack; 
template<bool... bs> 
using all_true = std::is_same<bool_pack<bs..., true>, bool_pack<true, bs...>>; 

static_assert(all_true<(Numbers == 0 || Numbers == 1)...>::value, ""); 

複雑な場合は、constexpr関数に式を抽出します。

+1

うわー、これは非常にクールなトリックのように見えます! – Venemo

+0

これはエレガントです! –

2

従来のforループをコンパイル時の値と一緒に使用することはできませんが、コンパイル時のコレクションに対してさまざまな方法で反復処理を行うことができます。あなたのケースでは、しかし、それはすべての単一の数を超える明示的にループする必要はありません:

coliru example

#include <type_traits> 

// We define a `conjunction<...>` helper that will evaluate to 
// a true boolean `std::integral_constant` if all passed types evaluate 
// to true. 

template <typename...> 
struct conjunction : std::true_type 
{ 
}; 

template <typename T> 
struct conjunction<T> : T 
{ 
}; 

template <typename T, typename... Ts> 
struct conjunction<T, Ts...> 
    : std::conditional_t<T::value != false, conjunction<Ts...>, T> 
{ 
}; 

// Define `constexpr` predicates: 

template <int T> 
constexpr bool is_zero_or_one() 
{ 
    return T == 0 || T == 1; 
} 

template <int... Ts> 
constexpr bool all_are_zero_or_one() 
{ 
    // Using variadic pack expansion and `conjunction` we can 
    // simulate an `and` left fold over the parameter pack: 
    return conjunction< 
     std::integral_constant<bool, is_zero_or_one<Ts>()>... 
    >{}; 
} 

int main() 
{ 
    static_assert(all_are_zero_or_one<0, 1, 0, 1, 0, 0>(), ""); 
    static_assert(!all_are_zero_or_one<2, 1, 0, 1, 0, 0>(), ""); 
} 

の場合:あなたは数字だけ0または1であることを確認するためにパックの拡張を使用することができますコンパイル時の要素のコレクションを明示的に反復する方法を探しているので、次のリソースを調べることをお勧めします。

boost::hana - 現代のメタプログラミングコンパイル時に「伝統的な」命令文法を使用した計算を可能にするライブラリです。

My CppCon 2015 talk: for_each_argument explained and expanded - std::tupleと "タイプ値エンコーディング"パラダイムを使用すると、コンパイル時の数値をタプルに格納し、コンパイル時に反復処理することができます。私の話は、このような方法で反復する可能性のある方法を示しています。

+0

おかげさまでヴィットーリオ!いいえ、要素をループしたくないので、コンパイル時にすべての数値が「0」または「1」であることを保証する方法を探しています。 – Venemo

2

静的検証は、このような再帰的テンプレートヘルパーで実装できます。次に、無効な数値を含むシーケンスを使用してコードをコンパイルしようとすると、静的なアサーションの失敗でコンパイラエラーが発生します。

#include <iostream> 

template<typename T, T... Numbers> 
struct ValidateSequence; 

template<typename T> 
struct ValidateSequence<T>{}; 

template<typename T, T Number, T... Numbers> 
struct ValidateSequence<T, Number, Numbers...> 
{ 
    static_assert(Number == 0 || Number == 1, "Invalid Number"); 

    ValidateSequence<T, Numbers...> rest; 
}; 

template<typename T, T... Numbers> 
class Sequence 
{ 
public: 
    constexpr static unsigned count = sizeof...(Numbers); 
    constexpr static T numbers[] = {Numbers...}; 

    ValidateSequence<T, Numbers...> validate; 
}; 

int main() 
{ 
    Sequence <int, 1, 2, 1, 2> sec; 

    std::cout << sec.count << std::endl; 
    return 0; 
} 
+0

私はこれが好きですが、あなたの周りにシャッフルを少ししても、どの引数が検証に失敗したかを明示的に伝えるより良いエラーメッセージを得ることができます* 'static_assert failed"無効な数値 "[...] 「ここをクリック」を確認してください。 [例](http://coliru.stacked-crooked.com/a/0291da8dc7ce2c0d) – melak47

0

さらに別の解決策:

template<typename T> 
constexpr bool IsOneOrZero(T&& t) { 
    return t == 0 || t == 1; 
} 

template<typename T, typename... Args> 
constexpr bool IsOneOrZero(T&& first, Args&&... args) { 
    return IsOneOrZero(std::forward<T>(first)) && IsOneOrZero(std::forward<Args>(args)...); 
} 

template<typename T, T First, T... Numbers> 
class Sequence final { 

    // Unpack parameter pack into a constexpr array 
    constexpr static T count = sizeof...(Numbers);   
    constexpr static T numbers[count] = { Numbers... }; 

    static_assert(IsOneOrZero(First, Numbers...), "ERROR"); 
}; 
5

シンプルなC++ 14解決策:

template <typename T, T ... Numbers> 
class Sequence final { 
    static constexpr bool is_all_zero_or_one(std::initializer_list<T> list) { 
    for (auto elem : list) { 
     if (elem != 0 && elem != 1) return false; 
    } 
    return true; 
    } 

    static_assert(is_all_zero_or_one({Numbers...}), 
       "Only zeroes and ones are allowed."); 
}; 
+0

これはC++ 11で動作しませんか? – Venemo

+1

@Venemo:いいえC++ 11のconstexpr関数は 'for'ループを持つことはできません。 – mpark

関連する問題