【问题标题】:static_assert if expressions is constexprstatic_assert 如果表达式是 constexpr
【发布时间】:2016-09-16 04:13:23
【问题描述】:

我想创建一个类模板

template <class T>
class X {
  // here I'll use T::value (among other things)
};

T::value 通常是 constexpr 静态变量,但并非总是如此。 T::value 必须是正值,所以我希望在编译时尽可能让人们知道。

如果 T::value 总是 constexpr,我会添加 static_assert 喜欢

static_assert(T::value > 0, "need positive number");

是否可以仅在 T::value 为 constexpr 的情况下添加此 static_assert?

【问题讨论】:

    标签: c++ sfinae typetraits static-assert


    【解决方案1】:

    我们可以写一个is_valid模板函数(想出一个更好的名字),有两个重载:

    template <typename T, int N = T::value>
    constexpr bool is_valid(int) {
        return N > 0;
    }
    
    template <typename T>
    constexpr bool is_valid(...) {
        return true;
    }
    

    第一个重载只有在T::value 是一个常量表达式时才有效,否则它将被 SFINAE 淘汰。第二个重载无论如何都是有效的,所以我们用一个虚拟的int 参数来消除重载的歧义。

    现在我们像这样测试它:

    static_assert(is_valid<T>(0), "need positive number");
    

    Live Demo

    【讨论】:

    • 酷。如果实现不必知道它依赖于 T::value 会更酷(即可以写static_assert(some_func(T::value &gt; 0), "")static_assert(some_func(T::other_value &gt; T::third_value()), "")
    • @RiaD 类似this?那时,您实际上是在实施概念;)。
    • 好吧,这并没有好多少,因为仍然需要为每个条件创建一个特征,但是是的,我的意思是类似的
    • 理想的函数类似于return_value_if_constexpr_otherwise_true。如果有基于 constexpr 的重载,写起来会很容易:)
    • 遗憾的是,template &lt;class F&gt; constexpr auto is_constexpr(F f) -&gt; std::enable_if_t&lt;((void)f(),true), std::true_type&gt; 似乎不起作用(即使使用 constexpr lambda),因为您不能在函数体开始之前使用参数。
    【解决方案2】:

    这在 clang++ 上对我有用:

    #include <type_traits>
    
    // The default case, returns true if the value is not constant.
    template <typename T, typename = void>
    struct IsNonConstantOrPositive {
        static const bool value = true;
    };
    
    // The `constexpr` case. We check if we can evaluate `T::value == 0` which can only
    // be evaluated at compile-time if `T::value` is constant. The whole `enable_if` thing
    // is to provide a substitution to ensure SFINAE.
    template <typename T>
    struct IsNonConstantOrPositive<T, typename std::enable_if<T::value==0||true>::type> {
        static const bool value = T::value > 0;
    };
    
    template <typename T>
    struct X {
        static_assert(IsNonConstantOrPositive<T>::value, "T::value should be positive");
    };
    

    例子:

    struct A {  // const > 0, should succeed
        static const int value = 123;
    };
    
    struct B {  // const <= 0, should fail
        static const int value = -1234;
    };
    
    struct C {   // non-const, should succeed
        static int value;
    };
    
    int main() {
        X<A> a;     // ok
        //X<B> b;   // error
        X<C> c;     // ok
    }
    

    【讨论】:

    • 您能解释您的解决方案吗?惊讶地看到 30 万用户提供原始代码转储。你应该知道这不是我们在这里所做的。
    【解决方案3】:

    我认为对变量的 const(expr)-ness 进行测试会很好,可以像这样使用:

    struct T {
        ...
        static_assert(!IS_CONSTANT_VAR(value) || value > 0, "trouble is afoot");
    };
    

    下面的实现使用与 kennytm 的解决方案类似的策略来失败非常量引用。它适用于 Clang 和 GCC。

    #include <type_traits> // enable_if
    
    template<typename T, T& var, typename = void> struct is_constant_var_impl {
        static constexpr bool value = false;
    };
    template<typename T, T& var>
    struct is_constant_var_impl <T, var, typename std::enable_if<(double)var == (double)var>::type> {
        // (double) cast above to thwart GCC's agressive constant folding;
        // perhaps could be removed with a bit more finesse
        static constexpr bool value = true;
    };
    #define IS_CONSTANT_VAR(...) (is_constant_var_impl<decltype(__VA_ARGS__), (__VA_ARGS__)>::value)
    

    优点

    • 模板可以在不同的类或静态成员名称中重复使用
    • static_assert 中的自解释代码

    缺点

    • 在 MSVC 中不起作用
    • (也许?)使用 C++14
    • 总价值

    【讨论】:

    • few cmets 1) 你能解释为什么static_assert(!IS_CONSTANT_VAR(value) || value &gt; 0, "") 工作,考虑到它取决于价值。有保障吗? (我检查并在我的编译器上运行)(它基本上与我认为不应该工作的 static_assert(true || value > 0, "") 相同。2)来自 c++14 的内容,它编译得很好std=c++11 for me (on clang) 3) 我最好将命名 arg 用于宏而不是 VA_ARGS 只支持单个参数
    • @RiaD (1) || 的短路效应在编译时也起作用,因此如果第一个参数为真,它不依赖于value。但我不能保证任何事情:) (2) 嗯,我以为我看到了一些需要 C++14 的 constexpr 或引用参数,但现在我找不到它是什么。但它在 g++ -std=c++11 中失败了。 (3) 由于 C++ 可以使用不带括号的逗号(来自模板参数)的表达式,我喜欢这样做,所以它仍然可以使用这些,但这是一个品味问题。
    猜你喜欢
    • 2020-01-08
    • 2016-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多