Conditional compilation of templates

大城市里の小女人 提交于 2019-12-02 21:28:06

问题


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_asserts, 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_asserting 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!