面试题答案
一键面试实现原理
- 编译期多态:利用C++模板元编程的编译期计算特性,通过模板特化和模板参数推导,在编译期确定不同类型的最佳输出策略。这样避免了运行时的虚函数调用开销,提高性能。
- 类型萃取:使用类型萃取技术,从类型中提取相关信息,比如是否为基础类型、是否有特定接口等,以此来决定如何进行输出操作。这有助于提高代码的通用性,能够适配不同类型。
- SFINAE(Substitution Failure Is Not An Error):利用SFINAE原则,通过模板参数替换失败来排除不适用的模板重载,从而在编译期选择最合适的输出策略。
代码示例
假设我们有一个复杂的自定义类层次结构,包含三个类:Base
、Derived1
继承自 Base
、Derived2
继承自 Derived1
。
#include <iostream>
#include <type_traits>
// 基类
class Base {
public:
virtual void print() const {
std::cout << "Base class" << std::endl;
}
};
// 派生类1
class Derived1 : public Base {
public:
void print() const override {
std::cout << "Derived1 class" << std::endl;
}
};
// 派生类2
class Derived2 : public Derived1 {
public:
void print() const override {
std::cout << "Derived2 class" << std::endl;
}
};
// 模板元编程辅助函数,用于判断类型是否有print成员函数
template<typename T, typename = void>
struct has_print : std::false_type {};
template<typename T>
struct has_print<T, std::void_t<decltype(std::declval<const T>().print())>> : std::true_type {};
// 通用的输出模板
template<typename T, typename = std::enable_if_t<!has_print<T>::value>>
std::ostream& operator<<(std::ostream& os, const T& obj) {
os << "Unsupported type";
return os;
}
// 针对有print成员函数的类型的输出模板
template<typename T, typename = std::enable_if_t<has_print<T>::value>>
std::ostream& operator<<(std::ostream& os, const T& obj) {
obj.print();
return os;
}
使用示例
int main() {
Base b;
Derived1 d1;
Derived2 d2;
std::cout << b << std::endl;
std::cout << d1 << std::endl;
std::cout << d2 << std::endl;
int num = 10;
std::cout << num << std::endl; // 处理基础类型
return 0;
}
编译期确定最佳输出策略说明
- 类型萃取:
has_print
模板类利用std::void_t
和decltype
来判断类型T
是否有print
成员函数。这是编译期的类型信息提取。 - SFINAE:
std::enable_if_t
结合has_print
的结果,通过SFINAE原则在编译期选择最合适的operator<<
重载。如果类型有print
成员函数,选择调用print
函数的重载;否则,选择输出 “Unsupported type” 的通用重载。这样就实现了在编译期根据类型特性确定最佳的输出策略,既提高了性能(避免运行时虚函数开销)又保证了通用性(能够处理多种类型)。