模板的条件编译
Conditional compilation of templates
我正在尝试让 static_assert 帮助我避免 C++11 中的空指针。
问题似乎是 C++11 要求编译器编译模板,即使它们没有被实例化。
我有以下代码:
#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.
}
我希望编译时没有错误。但是 static_assert 被编译并给我一个编译时错误。
只有当 ClassA 的第二次实例化在代码中时,它才会给我一个编译时错误。
在所有 C++ 标准中,模板都是分两个阶段编译的。第二阶段是实例化,但是第一阶段编译也可能失败。特别是第一阶段会检测到语法错误。
对于您的情况,更简单的解决方案是省略第二个实例化的主体。
另一种解决方案是在 static_assert
中使用 T
,因此编译器必须将求值延迟到阶段 2。简单地说:static_assert(sizeof(T)==0,
标准允许但不要求编译器诊断无法为其生成有效实例化的模板。范围从简单的语法错误到 static_assert
中常量 false
表达式的示例。 §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.
不过,我对所有这些 SFINAE 机器感到很困惑。一个简单的
template<typename T, typename... Us>
T* create_if_constructible(Us... args) { return new T(args...); }
如果 T
不能从给定的参数构造,则已经拒绝编译,所以我不确定这种复杂的迂回表述对你有何帮助 "avoid null pointers"。
无论如何,让选择第二个函数模板成为编译时错误的简单方法是显式删除它。
template<typename T, typename... Us>
std::enable_if_t< std::is_constructible<T, Us...>::value == false, T * >
create_if_constructible(Us... args) = delete;
或者,如果您偏爱 static_assert
s,可能是因为自定义错误消息,您必须确保理论上有一种方法可以生成模板的有效实例。这意味着 1) 你 static_assert
所依赖的必须取决于模板参数,并且 2) 理论上必须有一种方法可以使条件成为 true
。一种简单的方法是使用辅助模板:
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;
}
这里的关键点是编译器不能假定 always_false<T>::value
总是 false
因为后面总是有一个专门化将它设置为 true
,所以不允许在模板定义时拒绝它。
我正在尝试让 static_assert 帮助我避免 C++11 中的空指针。
问题似乎是 C++11 要求编译器编译模板,即使它们没有被实例化。
我有以下代码:
#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.
}
我希望编译时没有错误。但是 static_assert 被编译并给我一个编译时错误。
只有当 ClassA 的第二次实例化在代码中时,它才会给我一个编译时错误。
在所有 C++ 标准中,模板都是分两个阶段编译的。第二阶段是实例化,但是第一阶段编译也可能失败。特别是第一阶段会检测到语法错误。
对于您的情况,更简单的解决方案是省略第二个实例化的主体。
另一种解决方案是在 static_assert
中使用 T
,因此编译器必须将求值延迟到阶段 2。简单地说:static_assert(sizeof(T)==0,
标准允许但不要求编译器诊断无法为其生成有效实例化的模板。范围从简单的语法错误到 static_assert
中常量 false
表达式的示例。 §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.
不过,我对所有这些 SFINAE 机器感到很困惑。一个简单的
template<typename T, typename... Us>
T* create_if_constructible(Us... args) { return new T(args...); }
如果 T
不能从给定的参数构造,则已经拒绝编译,所以我不确定这种复杂的迂回表述对你有何帮助 "avoid null pointers"。
无论如何,让选择第二个函数模板成为编译时错误的简单方法是显式删除它。
template<typename T, typename... Us>
std::enable_if_t< std::is_constructible<T, Us...>::value == false, T * >
create_if_constructible(Us... args) = delete;
或者,如果您偏爱 static_assert
s,可能是因为自定义错误消息,您必须确保理论上有一种方法可以生成模板的有效实例。这意味着 1) 你 static_assert
所依赖的必须取决于模板参数,并且 2) 理论上必须有一种方法可以使条件成为 true
。一种简单的方法是使用辅助模板:
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;
}
这里的关键点是编译器不能假定 always_false<T>::value
总是 false
因为后面总是有一个专门化将它设置为 true
,所以不允许在模板定义时拒绝它。