【问题标题】:C++ check if statement can be evaluated constexprC++ 检查语句是否可以评估 constexpr
【发布时间】:2019-08-12 18:53:58
【问题描述】:

有没有一种方法可以决定是否可以对某事物进行 constexpr 评估,并将结果用作 constexpr 布尔值?我的简化用例如下:

template <typename base>
class derived
{
    template<size_t size>
    void do_stuff() { (...) }

    void do_stuff(size_t size) { (...) }
public:
    void execute()
    {
        if constexpr(is_constexpr(base::get_data())
        {
            do_stuff<base::get_data()>();
        }
        else
        {
            do_stuff(base::get_data());
        }
    }
}

我的目标是 C++2a。

我发现了以下 reddit 线程,但我不是宏的忠实粉丝。 https://www.reddit.com/r/cpp/comments/7c208c/is_constexpr_a_macro_that_check_if_an_expression/

【问题讨论】:

  • 嗯,if constexpr 的主体只有在 if constexpr 中的表达式在编译时为真时才会被计算。这就是你要找的吗?
  • 但是如果 if constexpr([test]) 中的测试在编译时无法评估怎么办?
  • 也许你可以用std::is_constant_evaluated做点什么?
  • @AartStuurman:什么是do_stuff,它可以在编译时或运行时运行,但它本身不应该是constexpr?将其设为constexpr 函数,并将get_data 的值作为参数传递给它不是更有意义吗?

标签: c++ template-meta-programming constexpr c++20 if-constexpr


【解决方案1】:

这是另一种解决方案,更通用(适用于任何表达式,无需每次都定义单独的模板)。

此解决方案利用 (1) 从 C++17 开始,lambda 表达式可以是 constexpr (2) 从 C++20 开始,无捕获 lambda 的类型是默认可构造的。

这个想法是,当且仅当Lambda{}() 可以出现在模板参数中时,才会选择返回true 的重载,这实际上要求lambda 调用是一个常量表达式。

template<class Lambda, int=(Lambda{}(), 0)>
constexpr bool is_constexpr(Lambda) { return true; }
constexpr bool is_constexpr(...) { return false; }

template <typename base>
class derived
{
    // ...

    void execute()
    {
        if constexpr(is_constexpr([]{ base::get_data(); }))
            do_stuff<base::get_data()>();
        else
            do_stuff(base::get_data());
    }
}

【讨论】:

  • 有趣的解决方案...这样您就可以获得与我的自定义类型特征相同的结果,但更综合,最重要的是,验证的确切表达式 (base::get_data()) 嵌入在参数中,并不难-在我的解决方案中编码。很不错。我必须记住它。
  • 我接受这个,因为它是对一般问题的回答。 max66 答案也非常有用(在非 c++2a 的情况下),但每次使用都需要重复 :)
  • 逗号运算符 SFINAE...我的脑子里嗡嗡作响。
  • 对问题的启发性解决方案。我一直在对此进行一些测试,并且我认为不需要使用逗号 SFINAE,并且我很确定template&lt;typename T, auto = T()() &gt; 形式的模板,其中我的T 是您的Lambda 同样足够了。
  • 再想一想,由于 lambda 的返回类型为 void,因此需要逗号 SFINE。 (在我的测试中,我使用了类似于 []{ base::get_data(); return true;} 的 lambda,它始终是非 void。
【解决方案2】:

不完全是你问的(我已经为 get_value() 静态方法开发了一个自定义类型特征......也许可以概括它,但目前我不知道如何)但我想您可以使用 SFINAE 并进行如下操作

#include <iostream>
#include <type_traits>

template <typename T>
constexpr auto icee_helper (int)
   -> decltype( std::integral_constant<decltype(T::get_data()), T::get_data()>{},
                std::true_type{} );

template <typename>
constexpr auto icee_helper (long)
   -> std::false_type;

template <typename T>
using isConstExprEval = decltype(icee_helper<T>(0));

template <typename base>
struct derived
 {
   template <std::size_t I>
   void do_stuff()
    { std::cout << "constexpr case (" << I << ')' << std::endl; }

   void do_stuff (std::size_t i)
    { std::cout << "not constexpr case (" << i << ')' << std::endl; }

   void execute ()
    {
      if constexpr ( isConstExprEval<base>::value )
         do_stuff<base::get_data()>();
      else
         do_stuff(base::get_data());
    }
 };

struct foo
 { static constexpr std::size_t get_data () { return 1u; } };

struct bar
 { static std::size_t get_data () { return 2u; } };

int main ()
 { 
   derived<foo>{}.execute(); // print "constexpr case (1)"
   derived<bar>{}.execute(); // print "not constexpr case (2)"
 }

【讨论】:

  • 这太疯狂了,这种逗号操作符的使用,long/int 重载... 点个赞吧。 ://
  • @matovitch - 永远不要低估逗号运算符的力量 }:‑)
  • 这是否适用于sizeof(long) 等于sizeof(int) 的平台?
  • @GregoryNisbet - 是的。因为,对于语言和编译器来说,它们仍然是不同的类型。
【解决方案3】:
template<auto> struct require_constant;
template<class T>
concept has_constexpr_data = requires { typename require_constant<T::get_data()>; };

这基本上是std::ranges::split_view使用的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-04
    • 1970-01-01
    相关资源
    最近更新 更多