static_assert 如果表达式是 constexpr
static_assert if expressions is constexpr
我想创建一个 class 模板
template <class T>
class X {
// here I'll use T::value (among other things)
};
T::value
通常是一个 constexpr 静态变量,但并非总是如此。 T::value
必须是正值,所以我想尽可能在编译期间让人们知道它。
如果 T::value
始终是 constexpr,我会添加 static_assert
,例如
static_assert(T::value > 0, "need positive number");
是否可以仅在 T::value
是 constexpr 的情况下添加此 static_assert?
这对我在 clang++ 上有效:
#include <type_traits>
// The default case, returns true if the value is not constant.
template <typename T, typename = void>
struct IsNonConstantOrPositive {
static const bool value = true;
};
// The `constexpr` case. We check if we can evaluate `T::value == 0` which can only
// be evaluated at compile-time if `T::value` is constant. The whole `enable_if` thing
// is to provide a substitution to ensure SFINAE.
template <typename T>
struct IsNonConstantOrPositive<T, typename std::enable_if<T::value==0||true>::type> {
static const bool value = T::value > 0;
};
template <typename T>
struct X {
static_assert(IsNonConstantOrPositive<T>::value, "T::value should be positive");
};
示例:
struct A { // const > 0, should succeed
static const int value = 123;
};
struct B { // const <= 0, should fail
static const int value = -1234;
};
struct C { // non-const, should succeed
static int value;
};
int main() {
X<A> a; // ok
//X<B> b; // error
X<C> c; // ok
}
我们可以写一个 is_valid
带有两个重载的模板函数(想出一个更好的名字):
template <typename T, int N = T::value>
constexpr bool is_valid(int) {
return N > 0;
}
template <typename T>
constexpr bool is_valid(...) {
return true;
}
第一个重载只有在T::value
是常量表达式时才有效,否则会被SFINAEd输出。无论如何,第二个重载都是有效的,因此我们使用虚拟 int
参数来消除重载的歧义。
现在我们这样测试:
static_assert(is_valid<T>(0), "need positive number");
我认为测试变量的 const(expr)-ness 会很好,可以像这样使用:
struct T {
...
static_assert(!IS_CONSTANT_VAR(value) || value > 0, "trouble is afoot");
};
下面的实现使用与 kennytm 的解决方案类似的策略来在非常量引用上失败。它适用于 Clang 和 GCC。
#include <type_traits> // enable_if
template<typename T, T& var, typename = void> struct is_constant_var_impl {
static constexpr bool value = false;
};
template<typename T, T& var>
struct is_constant_var_impl <T, var, typename std::enable_if<(double)var == (double)var>::type> {
// (double) cast above to thwart GCC's agressive constant folding;
// perhaps could be removed with a bit more finesse
static constexpr bool value = true;
};
#define IS_CONSTANT_VAR(...) (is_constant_var_impl<decltype(__VA_ARGS__), (__VA_ARGS__)>::value)
优点
- 模板可以在不同的 类 或静态成员名称中重复使用
static_assert
中的自解释代码
缺点
- 不适用于 MSVC
- (也许?)使用 C++14
- 毛
我想创建一个 class 模板
template <class T>
class X {
// here I'll use T::value (among other things)
};
T::value
通常是一个 constexpr 静态变量,但并非总是如此。 T::value
必须是正值,所以我想尽可能在编译期间让人们知道它。
如果 T::value
始终是 constexpr,我会添加 static_assert
,例如
static_assert(T::value > 0, "need positive number");
是否可以仅在 T::value
是 constexpr 的情况下添加此 static_assert?
这对我在 clang++ 上有效:
#include <type_traits>
// The default case, returns true if the value is not constant.
template <typename T, typename = void>
struct IsNonConstantOrPositive {
static const bool value = true;
};
// The `constexpr` case. We check if we can evaluate `T::value == 0` which can only
// be evaluated at compile-time if `T::value` is constant. The whole `enable_if` thing
// is to provide a substitution to ensure SFINAE.
template <typename T>
struct IsNonConstantOrPositive<T, typename std::enable_if<T::value==0||true>::type> {
static const bool value = T::value > 0;
};
template <typename T>
struct X {
static_assert(IsNonConstantOrPositive<T>::value, "T::value should be positive");
};
示例:
struct A { // const > 0, should succeed
static const int value = 123;
};
struct B { // const <= 0, should fail
static const int value = -1234;
};
struct C { // non-const, should succeed
static int value;
};
int main() {
X<A> a; // ok
//X<B> b; // error
X<C> c; // ok
}
我们可以写一个 is_valid
带有两个重载的模板函数(想出一个更好的名字):
template <typename T, int N = T::value>
constexpr bool is_valid(int) {
return N > 0;
}
template <typename T>
constexpr bool is_valid(...) {
return true;
}
第一个重载只有在T::value
是常量表达式时才有效,否则会被SFINAEd输出。无论如何,第二个重载都是有效的,因此我们使用虚拟 int
参数来消除重载的歧义。
现在我们这样测试:
static_assert(is_valid<T>(0), "need positive number");
我认为测试变量的 const(expr)-ness 会很好,可以像这样使用:
struct T {
...
static_assert(!IS_CONSTANT_VAR(value) || value > 0, "trouble is afoot");
};
下面的实现使用与 kennytm 的解决方案类似的策略来在非常量引用上失败。它适用于 Clang 和 GCC。
#include <type_traits> // enable_if
template<typename T, T& var, typename = void> struct is_constant_var_impl {
static constexpr bool value = false;
};
template<typename T, T& var>
struct is_constant_var_impl <T, var, typename std::enable_if<(double)var == (double)var>::type> {
// (double) cast above to thwart GCC's agressive constant folding;
// perhaps could be removed with a bit more finesse
static constexpr bool value = true;
};
#define IS_CONSTANT_VAR(...) (is_constant_var_impl<decltype(__VA_ARGS__), (__VA_ARGS__)>::value)
优点
- 模板可以在不同的 类 或静态成员名称中重复使用
static_assert
中的自解释代码
缺点
- 不适用于 MSVC
- (也许?)使用 C++14
- 毛