How do you static_assert the values in a parameter pack of a variadic template?
Simple C++14 solution:
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.");
};
I'll throw in @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, "");
Extract the expression into a constexpr
function if it gets complex.
a one-line c++17 solution based on @T.C.'s answer.
#include <type_traits>
static_assert(std::conjunction<std::bool_constant<(Numbers == 0 || Numbers == 1)>...>::value, "");
actually this could be done with c++11, given both std::conjunction and std::bool_constant are purely library feature and don't require any core language feature beyond c++11.
You cannot use a traditional for loop with compile-time values, but there are many ways you can iterate over a compile-time collection. In your case, however, it is not necessary to explicitly loop over every single number: you can use pack expansion to make sure the numbers are only 0
or 1
:
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>(), "");
}
If you are looking for an explicit way to iterate over a compile-time collection of elements, I suggest you to look into the following resources:
boost::hana - a modern metaprogramming library that allows compile-time computations using "traditional" imperative syntax.
My CppCon 2015 talk: for_each_argument explained and expanded - using std::tuple
and the "type-value encoding" paradigm you can store compile-time numerical values in a tuple and iterate over it at compile time. My talk shows a possible way to iterate in such a way.