c++:可以在不知道std::_Container_base是map还是vector的情况下处理它吗?

c++: can i process std::_Container_base without knowing if it is a map or a vector?

本文关键字:还是 情况下 vector 处理 map base 不知道 std c++ Container      更新时间:2023-10-16

猜我有

class C1 : public B { /*...*/ };
class C2 : public B { /*...*/ };
std::map<std::string, C1> myMap;
std::vector<C2> myVector;

是否有一种方法(以及语法)来调用函数foo…

  • 只需要处理B
  • 的功能
  • 只需要在地图和矢量的所有元素上处理它们,而不关心它们是如何组织的?

std::vectorstd::map都是std::_Container_base的,但我不知道如何编写(伪代码)的语法:

void foo(std::_Container_base-of-Bs)

编辑:这是_Container_base,不是_Tee

c++的方法是使用模板和迭代器。

template <typename ForwardIterator>
void process_bs(ForwardIterator first, ForwardIterator last) {
    std::for_each(first, last, [](B& b) {
        // do something to b here
    });
}

对于vector、list、deque和set,可以使用begin和end简单地调用:

process_bs(v.begin(), v.end());

对于map,元素类型是pair<const Key, Value>,因此必须调整迭代器。您可以在Boost中使用它。范围,例如:

#include <boost/range/adaptor/map.hpp>
auto values = m | boost::adaptors::map_values;
process_bs(values.begin(), values.end());

EDIT:以下是对变通方法的调查,而实际问题并未在其中得到回答。所以这就是答案:我不知道是否可以处理std::_Container_base而不知道它是映射还是向量

我在网上找不到任何关于std::_Container_base的合理的东西,特别是没有c++标准的东西,所以我猜它源于一个特定的编译器实现。



vectormap是完全不同的存储方案。我建议您不要在同一上下文中通用地使用它们。也就是说,从一开始你就可以编写函数模板

template<typename T> foo(T&& t) { /* takes a vector and a map */ }

,但至少当你访问operator[]时,它们的行为会有所不同。这样既不直观,又容易出错。

然而,这并不意味着你不能把这两种方法结合起来——在size()operator[](int)和可能的其他东西上抽象,比如一些插入机制。

例如,在我最近的一些代码中,我有vector -存储方案(在底层使用std::vector),以及一个分段常量向量(使用std::map)。如果您想这样做,您可以从一个公共基类

派生这两个
template<typename T>
struct ContainerBase
{
    virtual int size() const = 0;
    virtual T operator[](int) const = 0;
    virtual void insert(int, T) = 0;  //if required
};

,然后在派生类VectorMap中设置所需的功能。

template<typename T>
struct Vector
{
    virtual T operator[](int i) const { return _v[i]; }        
    virtual T size() const { return _v.size(); }        
    // ... insert and so on
    std::vector<T> _v;
};
template<typename T>
struct Map
{
    virtual T operator[](int i) const
    {
       return *std::lower_bound(i);  //add further checks if nothing is found
    }        
    virtual T size() const { return _v.rbegin()->first; // return highest index }        
    // ... insert and so on
    std::map<int, T> _v;
};

Map的实现只是一个草图。你应该选择一些合理的行为。

这样,很容易设置一个函数foo(ContainerBase&),它适用于VectorMap

要在foo函数中透明地使用B子类,您可以这样做:

#include <iostream>
#include <map>
#include <vector>
#include <string>
#include <utility>
struct B{
    int b_member;
};
class C1 : public B { /*...*/ };
class C2 : public B { /*...*/ };
std::map<std::string, C1> myMap;
std::vector<C2> myVector;
// all the magic is into get_B specializations
template<typename E, typename std::enable_if<std::is_base_of<B, E>::value>::type* a = nullptr>
B& get_B(E& elem)
{
    return elem;
}
template<typename E, typename std::enable_if<std::is_base_of<B, typename E::second_type>::value>::type* a = nullptr>
B& get_B(E& elem)
{
    return elem.second;
}
// foo can call get_B to hide implementation details of the container
template<typename T>
void foo( T& container)
{
    for(auto& elem : container)
    {
        std::cout << get_B(elem).b_member << 'n';
    }
}

int main()
{
    myVector.resize(10);
    myMap["one"] = {};
    foo(myMap);
    foo(myVector);
}

由于SFINAE, foo使用get_B的正确专门化来获取对想要处理的B子类的引用。