问题
I am trying to get static_assert to help me avoid null pointers in C++11.
The problem seems to be that C++11 require the compiler to compile templates even if they are not instantiated.
I have the following code:
#include <type_traits>
template<typename T, typename... Us>
std::enable_if_t< std::is_constructible<T, Us...>::value == true, T * >
create_if_constructible(Us... args) { return new T(args...); }
template<typename T, typename... Us>
std::enable_if_t< std::is_constructible<T, Us...>::value == false, T * >
create_if_constructible(Us... args) {
static_assert( false, "Class T constructor does not match argument list.");
return nullptr;
}
struct ClassA {
ClassA(int a, string b) {}
};
void foo() {
ClassA *a = create_if_constructible<ClassA>(1, "Hello");
// ClassA *b = create_if_constructible<ClassA>(1, "Hello", "world"); // I want compile time error here.
}
I would like this to compile without error. But the static_assert is compiled and gives me a compile time error.
Only if the the second instantiation of the ClassA is in the code should it give me a compile time error.
回答1:
The standard permits, but does not require, compilers to diagnose templates for which no valid instantiation can be generated. This can range from simple syntax errors to your example of a constant false
expression in a static_assert
. §14.6 [temp.res]/p8:
If no valid specialization can be generated for a template, and that template is not instantiated, the template is ill-formed, no diagnostic required.
I'm rather baffled by all this SFINAE machinery, though. A simple
template<typename T, typename... Us>
T* create_if_constructible(Us... args) { return new T(args...); }
already refuses to compile if T
is not constructible from the parameter given, so I'm not sure how this complex circumlocution will help you "avoid null pointers".
Regardless, a simple way to make choosing the second function template a compile-time error is to explicitly delete it.
template<typename T, typename... Us>
std::enable_if_t< std::is_constructible<T, Us...>::value == false, T * >
create_if_constructible(Us... args) = delete;
Alternatively, if you are partial to static_assert
s, perhaps because of the custom error message, you must ensure that there is theoretically a way to generate a valid instantiation of your template. That means that 1) what you are static_assert
ing on must depend on a template argument, and 2) there must be theoretically a way for the condition to be true
. A simple way is to use an auxiliary template:
template<class> class always_false : std::false_type {};
template<typename T, typename... Us>
std::enable_if_t< std::is_constructible<T, Us...>::value == false, T * >
create_if_constructible(Us... args) {
static_assert( always_false<T>::value, "Class T constructor does not match argument list.");
return nullptr;
}
The key point here is that the compiler cannot assume that always_false<T>::value
is always false
because it is always possible that there's a specialization later that sets it to true
, and so it is not allowed to reject this at template definition time.
回答2:
In all C++ standards ever, templates are compiled in two phases. The second phase is instantiation, but compilation can also fail in phase 1. In particular, syntax errors are detected in phase 1.
In your case, the simpler solution is to leave out the body of the second instantiation.
Another solution is to use T
in the static_assert
, so the compiler must delay evaluation to phase 2. Trivially: static_assert(sizeof(T)==0,
来源:https://stackoverflow.com/questions/29468724/conditional-compilation-of-templates