面试题答案
一键面试SFINAE 改变模板参数类型推导过程的原理
- 基本原理:SFINAE(Substitution Failure Is Not An Error)即替换失败不是错误。在模板实例化过程中,如果对模板参数进行替换时导致类型不匹配等错误,这种失败不会导致编译错误,而是使该模板被忽略,编译器继续寻找其他可行的模板。
- 复杂类型 - 模板模板参数:当存在模板模板参数时,SFINAE 可根据模板模板参数所满足的约束条件来推导类型。例如,假设有一个外层模板接受一个模板模板参数,这个模板模板参数可能有不同的实例化形式,但只有满足特定条件(如具有特定成员类型或成员函数)的实例化才会被正确推导。如果在推导过程中,对模板模板参数的替换导致某些成员类型或函数不存在等情况,依据 SFINAE 机制,此模板实例化将被忽略。
- 复杂类型 - 可变参数模板:在可变参数模板中,SFINAE 可用于对参数包中的参数进行类型推导和筛选。通过在模板参数列表中添加约束条件,只有当参数包中的参数满足这些条件时,模板才会被正确实例化。例如,可以检查参数包中的每个类型是否具有特定成员函数或类型,若不满足,则根据 SFINAE 机制忽略该模板实例化。
代码示例
#include <iostream>
#include <type_traits>
// 辅助模板,用于检测类型是否有成员类型 "type"
template <typename T, typename = void>
struct has_type : std::false_type {};
template <typename T>
struct has_type<T, std::void_t<typename T::type>> : std::true_type {};
// 主模板
template <typename T, typename = std::enable_if_t<has_type<T>::value>>
void print_type() {
std::cout << "Type has 'type' member type." << std::endl;
}
// 备用模板,用于不满足条件的类型
template <typename T>
void print_type() {
std::cout << "Type does not have 'type' member type." << std::endl;
}
struct A {
using type = int;
};
struct B {};
int main() {
print_type<A>();
print_type<B>();
return 0;
}
在上述代码中:
has_type
模板利用 SFINAE 来检测类型T
是否有成员类型type
。如果T
有type
成员类型,std::void_t<typename T::type>
会成功实例化,has_type<T>
为std::true_type
;否则为std::false_type
。print_type
主模板通过std::enable_if_t<has_type<T>::value>
来约束模板实例化,只有当T
满足has_type
检测时才会被实例化。print_type
备用模板用于不满足主模板约束的类型。- 在
main
函数中,print_type<A>()
会调用主模板,因为A
满足条件;print_type<B>()
会调用备用模板,因为B
不满足条件。这展示了 SFINAE 如何通过改变类型推导来选择正确的模板实例化。