如何static_assert变量模板的参数包中的值?

时间:2016-04-29 08:18:49

标签: c++ c++11 variadic-templates 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参数包只包含特定数字。为了这个例子,假设我只想允许01

所以我想做类似的事情:

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循环。我很确定必须有某种语法,但我无法弄明白。

我更喜欢使用可以编译C ++ 11编译器(或者可能是C ++ 14编译器,如果它在C ++ 11中不可行)的东西。

7 个答案:

答案 0 :(得分:21)

我会投入@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 :(得分:5)

Simple C ++ 14解决方案:

 var intwstatus= $('#intwstatus').val();
        alert(intwstatus);

答案 2 :(得分:2)

您不能将传统的for循环与编译时值一起使用,但是有很多方法可以迭代编译时集合。但是,在您的情况下,没有必要明确循环每个数字:您可以使用包扩展来确保数字仅为01

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>(), "");
}

如果您正在寻找一种迭代编译时元素集合的明确方法,我建议您查看以下资源:

boost::hana - 一个现代元编程库,允许使用&#34;传统&#34;编译时计算。命令式语法。

My CppCon 2015 talk: for_each_argument explained and expanded - 使用std::tuple和&#34;类型值编码&#34;范例,您可以将编译时数值存储在元组中,并在编译时迭代它。我的演讲显示了以这种方式迭代的可能方式。

答案 3 :(得分: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;
}

答案 4 :(得分:1)

  • C ++ 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

答案 5 :(得分: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");
};

答案 6 :(得分:0)

基于@T.C.'s answer的单行c ++ 17解决方案。

#include <type_traits>
static_assert(std::conjunction<std::bool_constant<(Numbers == 0 || Numbers == 1)>...>::value, "");

实际上,这可以使用c ++ 11来完成,因为std :: conjunction和std :: bool_constant都是纯粹的 库功能 ,并且不需要任何<除c ++ 11之外的em> 核心语言功能