C++ 检查语句是否可以被评估 constexpr
C++ check if statement can be evaluated constexpr
有没有一种方法可以决定某些东西是否可以被 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/
不完全是你问的(我已经开发了一个特定于 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)"
}
template<auto> struct require_constant;
template<class T>
concept has_constexpr_data = requires { typename require_constant<T::get_data()>; };
这基本上就是std::ranges::split_view
所使用的。
这是另一种解决方案,更通用(适用于任何表达式,无需每次都定义单独的模板)。
此解决方案利用了 (1) 从 C++17 开始,lambda 表达式可以是 constexpr (2) 从 C++20 开始,无捕获 lambda 的类型是默认可构造的。
这个想法是,当且仅当 Lambda{}()
可以出现在模板参数中时,才会选择 returns 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());
}
}
有没有一种方法可以决定某些东西是否可以被 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/
不完全是你问的(我已经开发了一个特定于 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)"
}
template<auto> struct require_constant;
template<class T>
concept has_constexpr_data = requires { typename require_constant<T::get_data()>; };
这基本上就是std::ranges::split_view
所使用的。
这是另一种解决方案,更通用(适用于任何表达式,无需每次都定义单独的模板)。
此解决方案利用了 (1) 从 C++17 开始,lambda 表达式可以是 constexpr (2) 从 C++20 开始,无捕获 lambda 的类型是默认可构造的。
这个想法是,当且仅当 Lambda{}()
可以出现在模板参数中时,才会选择 returns 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());
}
}