问题
I want to create a class template
template <class T>
class X {
// here I'll use T::value (among other things)
};
T::value
will often be a constexpr static variable, but not always. T::value
has to be positive value, so I want to let people know it during compilation, when possible.
If T::value
was always constexpr, I'd add static_assert
like
static_assert(T::value > 0, "need positive number");
Is it possible to add this static_assert only for cases when T::value
is constexpr?
回答1:
We can write an is_valid
template function (come up with a better name) with two overloads:
template <typename T, int N = T::value>
constexpr bool is_valid(int) {
return N > 0;
}
template <typename T>
constexpr bool is_valid(...) {
return true;
}
The first overload will only be valid if T::value
is a constant expression, otherwise it will be SFINAEd out. The second overload is valid no matter what, so we disambiguate the overload with a dummy int
parameter.
Now we test it like so:
static_assert(is_valid<T>(0), "need positive number");
Live Demo
回答2:
This works for me on 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");
};
Example:
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
}
回答3:
I think it would be nice to have a test for const(expr)-ness of a variable, to be used like:
struct T {
...
static_assert(!IS_CONSTANT_VAR(value) || value > 0, "trouble is afoot");
};
The implementation below uses a similar strategy to kennytm's solution to fail out on non-constant references. It works in Clang and 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)
Pros
- Template can be reused across different classes or static member names
- Self-explanatory code in the
static_assert
Cons
- Does not work in MSVC
- (Maybe?) Uses C++14
- Gross
来源:https://stackoverflow.com/questions/37320548/static-assert-if-expressions-is-constexpr