Как вы статично задаете значения в пакете параметров вариационного шаблона?
Я создаю вариационный шаблон.
Скажем, у меня есть что-то вроде этого:
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_assert
не работает с циклом for
. Я уверен, что для этого должен быть какой-то синтаксис, но я не смог понять это.
Я бы предпочел использовать что-то, что компилируется с компилятором С++ 11 (или, возможно, компилятором С++ 14, если он не выполним в С++ 11).
Ответы
Ответ 1
Я напишу @Columbo bool_pack
трюк.
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
, если оно становится сложным.
Ответ 2
Простое решение С++ 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.");
};
Ответ 3
Вы не можете использовать традиционный цикл for с значениями времени компиляции, но есть много способов, которыми вы можете перебирать сборку времени компиляции. В вашем случае, однако, нет необходимости явно перебирать каждое отдельное число: вы можете использовать расширение пакета, чтобы убедиться, что только цифры 0
или 1
:
пример coliru
#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>(), "");
}
Если вы ищете явный способ итерации по набору элементов времени компиляции, я предлагаю вам изучить следующие ресурсы:
boost:: hana - современная библиотека метапрограммирования, которая позволяет вычисления времени компиляции с использованием "традиционного" императивного синтаксиса.
My CppCon 2015 talk: for_each_argument объясняется и расширяется - используя std::tuple
и парадигму типа кодирования, которую вы можете хранить, временные числовые значения в кортеже и перебирать его во время компиляции. Мой разговор показывает возможный способ итерации таким образом.
Ответ 4
Вы можете реализовать свою статическую проверку с помощью рекурсивного помощника шаблона, например. Затем, когда вы пытаетесь скомпилировать код с последовательностью, содержащей недопустимые числа, вы получите ошибку компилятора со статическим утверждением, если хотите.
#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;
}
Ответ 5
- С++ 11
-
msvc2015u3, gcc5.4, clang3.8
#include <cstdint>
#include <algorithm>
namespace utility {
template <typename T0>
inline constexpr bool is_all_true(T0 && v0)
{
return std::forward<T0>(v0) ? true : false;
}
template <typename T0, typename... Args>
inline constexpr bool is_all_true(T0 && v0, Args &&... args)
{
return (std::forward<T0>(v0) ? true : false) && is_all_true(std::forward<Args>(args)...);
}
template <typename T0>
inline constexpr bool is_all_false(T0 && v0)
{
return std::forward<T0>(v0) ? false : true;
}
template <typename T0, typename... Args>
inline constexpr bool is_all_false(T0 && v0, Args &&... args)
{
return (std::forward<T0>(v0) ? false : true) && is_all_false(std::forward<Args>(args)...);
}
template <typename T0>
inline constexpr bool is_any_true(T0 && v0)
{
return std::forward<T0>(v0) ? true : false;
}
template <typename T0, typename... Args>
inline constexpr bool is_any_true(T0 && v0, Args &&... args)
{
return (std::forward<T0>(v0) ? true : false) || is_any_true(std::forward<Args>(args)...);
}
template <typename T0>
inline constexpr bool is_any_false(T0 && v0)
{
return std::forward<T0>(v0) ? false : true;
}
template <typename T0, typename... Args>
inline constexpr bool is_any_false(T0 && v0, Args &&... args)
{
return (std::forward<T0>(v0) ? false : true) || is_any_false(std::forward<Args>(args)...);
}
}
'
GCC, лязг
static_assert(utility::is_all_true((Numbers == 0 || Numbers == 1)...), "Only ones and zeroes are allowed.");
msvc2015u3 (с обходным решением для: error C2059: syntax error: '...'
)
static constexpr const bool boo = utility::is_all_true((Numbers == 0 || Numbers == 1)...);
static_assert(boo, "Only ones and zeroes are allowed.");
https://godbolt.org/z/hcS9FY
Ответ 6
Еще одно решение:
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");
};