与模板成员函数的接口

Interface with template member function

本文关键字:接口 函数 成员      更新时间:2023-10-16

我有兴趣为C++实现一个类似Java集合的环境。我知道这不是一个好主意,等等,但我以后真的不想使用它,只是学习如何做一些高级OOP。

我的问题是我想要一个具有纯虚拟函数的基类模板collection<T>。这些函数中的一个应该是map(),它采用std::function<R(T)>。由于map()应该是虚拟的,我不知道应该使用哪种返回类型。collection<R>不可能,因为成员函数模板不能是虚拟的。

如何为我的collection<T>接口添加这样的map()成员函数?

如何为我的collection<T>接口添加这样的map成员函数?

简短的回答是:你没有。如果我有一些collection<int>,并且我想在上面map std::to_string,我需要生成一个collection<std::string>。但是vector_collection<int>需要产生vector_collection<std::string>list_collection<int>需要产生list_collection<std::string>,因此类型转换本身需要virtual。但是你不能有virtual成员函数模板,所以没有办法表达这一点。

为了实现这一点,您必须为放入容器中的所有对象都有一个公共的基类型,然后只需要有一个可以在它们之间转换的公共外观。也就是说,你实际上只有collection<unique_ptr<Object>>,其中map只是给了你一个不同的collection<unique_ptr<Object>>,而你只是把map你的collection_facade<int, collection<unique_ptr<Object>>>变成了collection_facade<std::string, collection<unique_ptr<Object>>>。只要做大量的工作,完全无视性能和类型安全,你就可以做到这一点。


这就是模板的优点。如果我想为vector这样的东西写map,我可以写:

template <class T, class A, class F, class R = std::result_of_t<F(T)>>
std::vector<R, A> map(std::vector<T, A> const& v, F f) {
    std::vector<R, A> mapped;
    mapped.reserve(v.size());
    for (T const& elem : v) {
        mapped.push_back(f(elem));
    }
    return mapped;
}

或:

template <class T, class A, class F, class R = std::result_of_t<F(T)>>
std::vector<R, A> map(std::vector<T, A> const& v, F f) {
    return std::vector<R, A>(
        boost::make_transform_iterator(v.begin(), f),
        boost::make_transform_iterator(v.end(), f)
        );
}

我必须分别为每个容器实现map(),但无论如何我都必须这样做。现在我不会放弃任何东西。此外,您多久编写一次与运行时容器无关的算法?

map实现为外部模板函数。例如,您可以将map分解为两个阶段,内部虚拟生产者和外部模板消费者。

template<typename T> struct Collection {
    // virtual T next(); // Java way
    // C++ way
    // In simplest cases you can rely on iterator pairs.
    struct const_iterator {
        T const &operator*() const;
        const_iterator &operator++();
    }
    virtual const_iterator begin() const;
    virtual const_iterator end() const;
};
template<typename R, typename T> Collection<R> map(
    Collection<T> const &coll, std::function<R(T)> const &f);

要实现本质上复杂的容器和一元组合,您甚至可以拒绝begin()end(),并编写显式(部分)模板专用化。