771 words
4 minutes
SFINAE
SFINAE
是 substitution failure is not an error
的缩写
- 有一个单词的字符串需要匹配一些条件
- 如果没有匹配上其中某一个条件,称为
failure
- 如果这个匹配的不是字符串,则称发生了
error
- 如果没有匹配上其中某一个条件,称为
注意区分
error
和failure
将模板形参替换为实参的过程就称为substitute
举个例子,需要
struct ICounter {
virtual void increase() = 0;
virtual ~ICounter() {}
};
struct Counter : public ICounter {
void increase() override {}
};
template <typename T>
void inc_counter(T& counter_obj) {
counter_obj.increase();
}
template <typename T>
void inc_counter(T& int_type_counter) {
++int_type_counter;
}
void do_sth() {
Counter counter_obj;
uint32_t cnt;
inc_counter(counter_obj);
inc_counter(cnt);
}
int main() {
do_sth();
}
这样写是不可以的,因为函数定义完全相同,会造成redefinition
可以通过 <type_traits>
中定义的方法来进行编译期的类型检查
#include <type_traits>
struct ICounter {
virtual void increase() = 0;
virtual ~ICounter() = default;
};
struct Counter : public ICounter {
void increase() override {}
};
template <typename T>
void inc_counter(T& counter_obj,
typename std::enable_if<std::is_base_of_v<ICounter, T> >::type* = nullptr) {
counter_obj.increase();
}
template <typename T>
void inc_counter(T& int_type_counter,
typename std::enable_if<std::is_integral_v<T> >::type* = nullptr) {
++int_type_counter;
}
void do_sth() {
Counter counter_obj;
uint32_t cnt;
inc_counter(counter_obj);
inc_counter(cnt);
}
int main() {
do_sth();
}
解释一下这个函数定义
template <typename T>
void inc_counter(T& counter_obj, typename std::enable_if<std::is_base_of<T, ICounter>::value>::type* = nullptr);
template <typename T>
:这是一个函数模板的定义,表示函数将接受一个模板类型参数T
。这意味着函数可以在调用时用不同的类型进行实例化。void inc_counter(T& counter_obj, ...)
:这是函数的声明,它定义了一个名为inc_counter
的函数,它接受两个参数。第一个参数counter_obj
是一个引用,它表示计数器对象,将用于执行自增操作。第二个参数是一个使用模板元编程技巧的默认参数(默认值为nullptr
),稍后我们将详细解释。typename std::enable_if<std::is_base_of<T, ICounter>::value>::type* = nullptr
这是一个奇妙的默认参数,我们分开来看:std::is_base_of<T, ICounter>::value
- 这部分使用类型特性和元编程,检查类型
T
是否是ICounter
类型的派生类(或者说,是否ICounter
是T
的基类)。如果是,std::is_base_of<T, ICounter>::value
为true,否则为false。
- 这部分使用类型特性和元编程,检查类型
typename std::enable_if<...>::type* = nullptr
std::enable_if
是一个元编程工具,它根据条件启用或禁用函数模板。如果std::is_base_of<T, ICounter>::value
为真(true),那么std::enable_if
的条件成立,允许函数被实例化。如果条件不成立,那么std::enable_if
将导致函数不可实例化。
= nullptr
- 这是默认参数的一部分,表示如果条件成立(即
T
是ICounter
的派生类),则默认参数为nullptr
。如果条件不成立,这个默认参数没有实际影响,因为它永远不会被使用。
- 这是默认参数的一部分,表示如果条件成立(即
因此,这个函数模板 inc_counter
允许你传递一个计数器对象作为参数,并且只有当该对象的类型是 ICounter
或其派生类时,函数才会被实例化。
也就是说,这个函数只能用于实现对 ICounter
类型对象的自增操作,以确保类型安全。如果你尝试传递不是 ICounter
类型的对象,编译器将会产生错误,因为函数无法被实例化。这有助于在编译阶段捕获潜在的类型错误。