constexpr 函数内的编译时或运行时检测

2024-03-14

当 C++11 中引入 constexpr 时,我很兴奋,但不幸的是我对其有用性做出了乐观的假设。我假设我们可以在任何地方使用 constexpr 来捕获文字编译时常量或文字编译时常量的任何 constexpr 结果,包括如下所示:

constexpr float MyMin(constexpr float a, constexpr float b) { return a<b?a:b; }

因为仅将函数的返回类型限定为 constexpr 并不限制其在编译时的使用,并且还必须在运行时可调用,所以我认为这将是确保 MyMin 只能与编译时评估常量一起使用的一种方法,这将确保编译器永远不会允许其在运行时执行,从而使我能够编写一个更运行时友好的 MyMin 版本,最好与使用 _mm_min_ss 内部函数的名称相同,确保编译器不会生成运行时分支代码。不幸的是,函数参数不能是 constexpr,因此这似乎无法完成,除非可以实现以下操作:

constexpr float MyMin(float a, float b)
{
#if __IS_COMPILE_TIME__
    return a<b?a:b;
#else
    return _mm_cvtss_f32(_mm_min_ss(_mm_set_ss(a),_mm_set_ss(b)));
#endif
}

我严重怀疑 MSVC++ 是否有这样的东西,但我希望 GCC 或 clang 至少有一些东西可以完成它,无论它看起来多么不优雅。

诚然,我提供的示例非常简单,但是如果您可以发挥您的想象力,在很多情况下您可以随意做一些事情,例如在您知道只能在编译时执行的函数中广泛使用分支语句,因为如果它在运行时执行,性能就会受到影响。


可以检测给定的函数调用表达式是否是常量表达式,从而在两种不同的实现之间进行选择。下面使用的通用 lambda 需要 C++14。

(这个答案是长出来的这个答案来自@Yakk https://stackoverflow.com/a/32398825/146041我去年问过的一个问题)。

我不确定我将标准推向了多远。这是在 clang 3.9 上测试的,但会导致 g++ 6.2 给出“内部编译器错误”。我将在下周发送错误报告(如果没有其他人先这样做的话!)

第一步是移动constexpr实施成struct as a constexpr static方法。更简单地说,您可以离开当前的constexpr按原样并从 a 调用它constexpr static一种新的方法struct.

struct StaticStruct {
    static constexpr float MyMin_constexpr (float a, float b) {
        return a<b?a:b;
    }
};

另外,定义这个(尽管它看起来没用!):

template<int>
using Void = void;

基本思想是Void<i>要求i是一个常量表达式。更准确地说,以下 lambda 仅在某些情况下才会具有合适的重载:

auto l = [](auto ty)-> Void<(decltype(ty)::   MyMin_constexpr(1,3)   ,0)>{};
                                              \------------------/
                                               testing if this
                                               expression is a
                                               constant expression.

我们可以打电话l仅当论证ty属于类型StaticStruct and如果我们表达兴趣(MyMin_constexpr(1,3)) 是一个常量表达式。如果我们更换1 or 3带有非常量参数,则通用 lambdal将丢失通过 SFINAE 的方法。

因此,以下两个测试是等效的:

  • Is StaticStruct::MyMin_constexpr(1,3) a 常量表达式?
  • Can l被称为通过l(StaticStruct{})?

简单地删除是很诱人的auto ty and decltype(ty)来自上面的 lambda。但这会产生硬错误(在非恒定情况下),而不是良好的替换失败。因此我们使用auto ty得到替换失败(我们可以有效地检测到)而不是错误。

下一个代码很容易返回std:true_type当且仅当f(我们的通用 lambda)可以这样调用a (StaticStruct):

template<typename F,typename A>
constexpr
auto
is_a_constant_expression(F&& f, A&& a)
    -> decltype( ( std::forward<F>(f)(std::forward<A>(a)) , std::true_type{} ) )
{ return {}; }
constexpr
std::false_type is_a_constant_expression(...)
{ return {}; }

接下来演示一下它的使用:

int main() {
    {
        auto should_be_true = is_a_constant_expression(
            [](auto ty)-> Void<(decltype(ty)::   MyMin_constexpr(1,3)   ,0)>{}
            , StaticStruct{});
        static_assert( should_be_true ,"");
    }
    {   
        float f = 3; // non-constexpr
        auto should_be_false = is_a_constant_expression(
            [](auto ty)-> Void<(decltype(ty)::   MyMin_constexpr(1,f)   ,0)>{}
            , StaticStruct{});
        static_assert(!should_be_false ,"");
    }
}

为了直接解决你原来的问题,我们可以首先定义一个宏来节省重复:

(我还没有测试过这个宏,对任何拼写错误表示歉意。)

#define IS_A_CONSTANT_EXPRESSION( EXPR )                \
     is_a_constant_expression(                          \
         [](auto ty)-> Void<(decltype(ty)::             \
              EXPR                         ,0)>{}       \
         , StaticStruct{})

在这个阶段,也许你可以简单地这样做:

#define MY_MIN(...)                                            \
    IS_A_CONSTANT_EXPRESSION( MyMin_constexpr(__VA_ARGS__) ) ? \
        StaticStruct :: MyMin_constexpr( __VA_ARGS__ )     :   \
                        MyMin_runtime  ( __VA_ARGS__ )

或者,如果您不相信编译器会优化std::true_type and std::false_type通过?:,那么也许:

constexpr
float MyMin(std::true_type, float a, float b) { // called if it is a constant expression
    return StaticStruct:: MyMin_constexpr(a,b);
}
float MyMin(std::false_type, float , float ) { // called if NOT a constant expression
    return                MyMin_runtime(a,b);
}

用这个宏代替:

#define MY_MIN(...)                                             \
  MyMin( IS_A_CONSTANT_EXPRESSION(MyMin_constexpr(__VA_ARGS__)) \
       , __VA_ARGS__)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

constexpr 函数内的编译时或运行时检测 的相关文章

随机推荐