可以使用Richard Smith 提出的基于缩小转换规则的方法来识别给定的静态存储变量是否为常量表达式。
我们可以在不缩小的情况下分配给unsigned int
aconsexpr
非负数 :int
unsigned int u {std::max(0, -3)}; // compiles, max is constexpr
但是,如果我们使用变量,则无法执行上述操作:
int a = 3;
unsigned int u {std::max(0, a)}; // compilation error, narrowing int to unsigned int
为了确定一个给定的是否int reference
是 const 表达式,我们可以测试它是否可以分配给一个而不用其正值或负值unsigned int
缩小。对于在编译时已知的任何值,这应该是可能的,即可以被视为常量表达式。int
template<const int& p> std::true_type
is_constexpr_impl(decltype((unsigned int){std::max(-p, p)}));
template<const int& p> std::false_type
is_constexpr_impl(...);
template<const int& p> using is_constexpr =
decltype(is_constexpr_impl<p>(0));
现在我们可以使用宏方法对运行时和编译时间进行不同的实现:
int foo_runtime(int num) {
return num;
}
constexpr int foo_compiletime(int num) {
return num + 1;
}
#define foo(X) (is_constexpr<X>()?foo_compiletime(X):foo_runtime(X))
如前所述,它将模拟 const 表达式的重载:
int main() {
static int a = 3;
static const int b = 42; // considered constexpr
static const int c = foo_runtime(42); // not constexpr
static constexpr int d = 4;
static constexpr int e = -2;
static int f = 0;
static const int g = 0; // considered constexpr
std::cout << foo(a) << std::endl;
std::cout << foo(b) << std::endl;
std::cout << foo(c) << std::endl;
std::cout << foo(d) << std::endl;
std::cout << foo(e) << std::endl;
std::cout << foo(f) << std::endl;
std::cout << foo(g) << std::endl;
}
上面很好,虽然不是很有用,因为它仅限于静态存储变量。但它确实存在基于constexpr
.
另一种实现相同的方法,而不依赖于缩小转换,可以是:
template<const int& p> std::true_type
is_constexpr_impl(std::array<int, std::max(p, -p)>);
template<const int& p> std::false_type
is_constexpr_impl(...);
template<const int& p> using is_constexpr =
decltype(is_constexpr_impl<p>(0));
上面的使用std::array
替换了使用简单的 c 数组,这对于使用这种方法的 gcc 来说效果不佳。
或者另一个 - 再次,不依赖于缩小规则 -也可以正常工作:
template<const int& p, typename T = void>
struct is_constexpr: std::false_type {};
template<const int& p>
struct is_constexpr<p, std::void_t<int[std::max(p,-p)+1]>>: std::true_type {};
请注意,如果我们尝试使用更简单的方法来达到同样的效果:
template<typename T>
struct is_constexpr: std::false_type {};
template<typename T>
struct is_constexpr<const T>: std::true_type {};
#define foo(X) (is_constexpr<decltype(X)>()?foo_compiletime(X):foo_runtime(X))
我们不会实现这条线的目标:
static const int c = foo_runtime(42); // const but not constexpr