可以检测给定的函数调用表达式是否是常量表达式,从而在两种不同的实现之间进行选择。下面使用的通用 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__)