MST

星途 面试题库

面试题:C++ SFINAE 在复杂模板元编程结构中的应用

假设有一个模板类 `MyContainer` 用于存储不同类型的数据。使用SFINAE技术实现一个模板成员函数 `processData`,当存储的数据类型是可迭代容器(如 `std::vector`、`std::list` 等)时,该函数遍历容器并对每个元素执行特定操作(例如求平方);当存储的数据类型是基本算术类型时,直接对该数据执行相同操作;若为其他类型则在编译期使该函数无效。请详细实现 `MyContainer` 类及其 `processData` 成员函数。
36.7万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试
#include <iostream>
#include <vector>
#include <list>
#include <type_traits>

template <typename T>
class MyContainer {
private:
    T data;

public:
    MyContainer(const T& value) : data(value) {}

    // 辅助模板,判断类型是否为可迭代容器
    template <typename U>
    static auto has_begin_end(U&& u) -> decltype(std::declval<U>().begin(), std::declval<U>().end(), std::true_type());
    template <typename U>
    static std::false_type has_begin_end(...);

    // SFINAE 辅助模板
    template <typename U, typename = void>
    struct is_iterable_container : std::false_type {};

    template <typename U>
    struct is_iterable_container<U, decltype(has_begin_end(std::declval<U>()))> : std::true_type {};

    // 针对可迭代容器的 processData 实现
    template <typename U = T, typename = std::enable_if_t<is_iterable_container<U>::value>>
    void processData() {
        for (auto& element : data) {
            element = element * element;
        }
    }

    // 针对基本算术类型的 processData 实现
    template <typename U = T, typename = std::enable_if_t<std::is_arithmetic<U>::value &&!is_iterable_container<U>::value>>
    void processData() {
        data = data * data;
    }
};

int main() {
    MyContainer<std::vector<int>> vecContainer({1, 2, 3});
    vecContainer.processData();
    for (int num : vecContainer) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    MyContainer<int> intContainer(5);
    intContainer.processData();
    std::cout << intContainer << std::endl;

    // 以下代码会在编译期报错,因为 MyContainer<std::string> 没有有效的 processData 函数
    // MyContainer<std::string> strContainer("test");
    // strContainer.processData();

    return 0;
}
  1. 判断可迭代容器
    • 使用 has_begin_end 模板函数,通过 decltype 检查类型是否有 beginend 成员函数,以此判断是否为可迭代容器。
    • is_iterable_container 模板类利用 has_begin_end 辅助判断类型是否为可迭代容器。
  2. processData 成员函数重载
    • 第一个 processData 模板函数通过 std::enable_if_t<is_iterable_container<U>::value> 来判断存储类型是否为可迭代容器,如果是,则遍历容器并对每个元素求平方。
    • 第二个 processData 模板函数通过 std::enable_if_t<std::is_arithmetic<U>::value &&!is_iterable_container<U>::value> 来判断存储类型是否为基本算术类型且不是可迭代容器,如果是,则直接对数据求平方。
  3. main 函数
    • 测试了 MyContainer 存储 std::vector<int>int 类型时 processData 函数的正确性。同时注释掉了存储 std::string 类型时的调用,因为 std::string 类型没有有效的 processData 函数,会在编译期报错。