面试题答案
一键面试#include <type_traits>
#include <memory>
#include <iostream>
#include <vector>
// 用于存储小型POD类型的栈上存储结构
template <typename T, size_t N>
struct StackStorage {
T data[N];
size_t size = 0;
void push(const T& value) {
if (size < N) {
data[size++] = value;
}
}
void pop() {
if (size > 0) {
--size;
}
}
T& operator[](size_t index) {
return data[index];
}
const T& operator[](size_t index) const {
return data[index];
}
};
// 用于存储大型复杂类型的堆上存储结构
template <typename T>
struct HeapStorage {
std::vector<T> data;
void push(const T& value) {
data.push_back(value);
}
void pop() {
if (!data.empty()) {
data.pop_back();
}
}
T& operator[](size_t index) {
return data[index];
}
const T& operator[](size_t index) const {
return data[index];
}
};
// 根据类型选择存储策略的模板
template <typename T>
struct StorageSelector {
using type = std::conditional_t<
std::is_pod<T>::value && sizeof(T) <= 16,
StackStorage<T, 10>,
HeapStorage<T>
>;
};
// 异构容器模板类
template <typename... Ts>
class HeterogeneousContainer {
private:
template <typename T, typename = void>
struct EnableIfIterable {
static const bool value = false;
};
template <typename T>
struct EnableIfIterable<T, std::void_t<decltype(std::begin(std::declval<T>())), decltype(std::end(std::declval<T>()))>> {
static const bool value = true;
};
template <typename T>
using EnableIfIterable_t = std::enable_if_t<EnableIfIterable<T>::value>;
template <typename T, typename = void>
struct IteratorTraits {
using type = void;
};
template <typename T>
struct IteratorTraits<T, std::void_t<decltype(std::begin(std::declval<T>()))>> {
using type = decltype(std::begin(std::declval<T>()));
};
template <typename T>
using IteratorTraits_t = typename IteratorTraits<T>::type;
template <size_t I, typename Func, typename... Args>
auto visit_helper(Func&& func, Args&&... args) {
if constexpr (I < sizeof...(Ts)) {
using T = std::tuple_element_t<I, std::tuple<Ts...>>;
auto& storage = std::get<StorageSelector<T>::type>(storages);
for (size_t j = 0; j < storage.size; ++j) {
auto result = std::invoke(func, storage[j], std::forward<Args>(args)...);
if (result) return result;
}
return visit_helper<I + 1>(std::forward<Func>(func), std::forward<Args>(args)...);
} else {
return std::optional<>();
}
}
template <typename T, typename Func, typename... Args>
auto visit_type_helper(Func&& func, Args&&... args) {
for (size_t i = 0; i < sizeof...(Ts); ++i) {
if constexpr (std::is_same_v<std::tuple_element_t<i, std::tuple<Ts...>>, T>) {
auto& storage = std::get<StorageSelector<T>::type>(storages);
for (size_t j = 0; j < storage.size; ++j) {
auto result = std::invoke(func, storage[j], std::forward<Args>(args)...);
if (result) return result;
}
}
}
return std::optional<>();
}
std::tuple<typename StorageSelector<Ts>::type...> storages;
public:
// 插入元素
template <typename T>
void insert(const T& value) {
using Storage = typename StorageSelector<T>::type;
std::get<Storage>(storages).push(value);
}
// 删除元素
template <typename T>
void remove(const T& value) {
using Storage = typename StorageSelector<T>::type;
auto& storage = std::get<Storage>(storages);
for (size_t i = 0; i < storage.size; ++i) {
if (storage[i] == value) {
storage[i] = storage[storage.size - 1];
storage.pop();
break;
}
}
}
// 访问元素
template <typename Func, typename... Args>
auto visit(Func&& func, Args&&... args) {
return visit_helper<0>(std::forward<Func>(func), std::forward<Args>(args)...);
}
template <typename T, typename Func, typename... Args>
auto visit_type(Func&& func, Args&&... args) {
return visit_type_helper<T>(std::forward<Func>(func), std::forward<Args>(args)...);
}
// 自适应遍历操作
template <typename Iter, typename Func, typename = EnableIfIterable_t<Iter>>
void traverse(Iter iter, Func&& func) {
using IT = IteratorTraits_t<Iter>;
for (IT it = std::begin(iter); it != std::end(iter); ++it) {
visit([&func, &it](auto& value) {
if constexpr (std::is_same_v<std::decay_t<decltype(value)>, std::decay_t<decltype(*it)>>) {
func(value, *it);
}
});
}
}
};
关键部分设计思路
-
存储策略选择:
- 使用
std::conditional_t
和std::is_pod
以及sizeof(T)
来判断类型是否为小型POD类型。如果是,则使用StackStorage
在栈上存储;否则使用HeapStorage
在堆上存储。 StorageSelector
模板类根据类型T
选择合适的存储策略。
- 使用
-
异构容器实现:
HeterogeneousContainer
模板类使用std::tuple
来存储不同类型的元素,每个元素的存储结构由StorageSelector
确定。insert
函数根据元素类型选择合适的存储结构并插入元素。remove
函数根据元素类型找到对应的存储结构并删除元素。visit
函数使用模板递归和std::invoke
对容器中的所有元素应用给定的函数对象,并返回第一个非空的结果。visit_type
函数则只对特定类型的元素应用函数对象。
-
自适应遍历操作:
- 使用SFINAE技术通过
EnableIfIterable
模板类来判断迭代器类型是否可迭代。只有可迭代的类型才能调用traverse
函数。 traverse
函数利用IteratorTraits
获取迭代器类型,并遍历迭代器,同时使用visit
函数对容器中与迭代器元素类型相同的元素应用给定的函数对象。
- 使用SFINAE技术通过
SFINAE的具体应用
-
判断类型是否可迭代:
EnableIfIterable
模板类利用std::void_t
和decltype
检查类型是否有std::begin
和std::end
成员函数,以此判断类型是否可迭代。只有可迭代的类型才能通过SFINAE约束,使得traverse
函数在编译期只对可迭代类型实例化。
-
获取迭代器类型:
IteratorTraits
模板类利用std::void_t
和decltype
获取迭代器类型。这使得traverse
函数能够在编译期获取迭代器类型,以便进行遍历操作。