如何使用模板根据类型将元素添加到各种容器

时间:2018-10-03 15:48:38

标签: c++ templates

我有一个很愚蠢的问题,但是我希望你能帮助我。

我有多个向量类,并且这些向量具有不同的存储类型。

class BaseClass{
  std::string Name;
}

class B : public BaseClass{

}
class C : public BaseClass{

}

class A{
  vector<std::pair<std::string, B>> V1;
  vector<std::pair<std::string, C>> V2;
}

在我的A类中,有一个模板函数可以向这些向量添加元素:

template <class T>  void addElement(T Obj);

我希望这种情况发生:

A a;
B b;
C c;


a.addElement<B>(b) -> then element b is added to vector V1
a.addElement<C>(c) -> then element c is added to vector V2

我想到了这样的东西:

template <class T>  void addElement(T Obj){
  std::pair<std::string, T> AddedPair(Obj.Name, Obj);

   if (typeid(T) == typeid(B)) 
    V1.push_back(AddedPair);

   if (typeid(T) == typeid(C)) 
    V2.push_back(AddedPair);

}

但是不幸的是,该代码无法编译,因为以某种方式将模板作为一个整体进行编译,如果我将B用作模板参数,那么尽管该程序永远无法达到可以进行这种转换的地步,但编译器无法将B转换为C :(

您有什么建议我该如何解决?我会非常感激的。

5 个答案:

答案 0 :(得分:2)

而不是拥有

template <class T>  void addElement(T Obj);

只需重载该函数即可。那会给你

void addElement(const B& Obj)
{
    V1.push_back({Obj.Name, Obj});
}

void addElement(const C& Obj)
{
    V2.push_back({Obj.Name, Obj});
}

这为您节省了专门化模板或需要C ++ 17和if constexpr在编译时做出决定的所有语法。


原因

template <class T>  void addElement(T Obj){
std::pair<std::string, T> AddedPair(Obj.Name, Obj);

    if (typeid(T) == typeid(B)) 
        V1.push_back(AddedPair);

    if (typeid(T) == typeid(C)) 
        V2.push_back(AddedPair);

}

如果if块需要有效(即使永远无法到达),则每个代码都不起作用,但这不是因为您要在向量中添加其他类型。 if constexpr有帮助,但我发现重载与输入一样多,并且使代码向后不兼容。

这意味着您将不得不像这样专门化模板

template <class T>  void addElement(T Obj);

template <>  void addElement(B Obj)
{
    V1.push_back({Obj.Name, Obj});
}   
template <>  void addElement(C Obj)
{
    V1.push_back({Obj.Name, Obj});
}

或使用if constexpr

template <class T>  void addElement(T Obj){
std::pair<std::string, T> AddedPair(Obj.Name, Obj);

    if constexpr(std::is_same_v<T, B>) 
        V1.push_back(AddedPair);

    if constexpr(std::is_same_v<T, C>) 
        V2.push_back(AddedPair);

}       

答案 1 :(得分:1)

这可能是Tagged Tuple library的用例。这样就可以按关联的类型对容器进行索引。因此,处理数十个相似的vector<std::pair<std::string, B>> V1;字段的代码变得通用:

#include <vtt/container/Tagged Tuple.hpp>

#include <string>
#include <type_traits>
#include <vector>
#include <utility>

class BaseClass{::std::string Name;};
class B : public BaseClass{};
class C : public BaseClass{};

class A
{
    public: template<typename x_Item> using
    t_Vector = ::std::vector<::std::pair<::std::string, x_Item>>;

    public: using
    t_Vectors = ::n_vtt::n_container::t_TaggedTuple
    <// index type -> value type mapping
        B, t_Vector<B>
    ,   C, t_Vector<C>
    >;

    private: t_Vectors m_vectors;

    public: template<typename x_Item> void
    Add_Item(x_Item && item)
    {
        m_vectors
            // invoke either Get_MutableItem<B> or Get_MutableItem<C>
            .Get_MutableItem<::std::remove_reference_t<::std::remove_cv_t<x_Item>>>()
            // add item into corresponding std::vector
            .emplace_back(::std::string{}, ::std::forward<x_Item>(item));
    }
};

int main()
{
    A a;
    a.Add_Item(B{});
    C c{};
    a.Add_Item(c);
    return 0;
}

答案 2 :(得分:1)

您可以将“通用获取器”用作向量:

class A
{
public:

    template <typename T>
    std::vector<std::pair<std::string, T>>& getVector() {
        auto vectors = std::tie(V1, V2);
        return std::get<std::vector<std::pair<std::string, T>>&>(vectors);
    }

    template <class T>
    void addElement(T Obj) {
        getVector<T>().emplace_back(Obj.Name, Obj);
    }

    std::vector<std::pair<std::string, B>> V1;
    std::vector<std::pair<std::string, C>> V2;
};

直接更改您的会员资格可能std::tuple。 并且您可能想对整个课程进行模板化:

template <typename ... Ts>
class A_Impl
{
private:
    template <typename T>
    decltype(auto) getVector() const {
        return std::get<std::vector<std::pair<std::string, T>>>(Vs);
    }
    template <typename T>
    decltype(auto) getVector() {
        return std::get<std::vector<std::pair<std::string, T>>>(Vs);
    }
public:

    template <class T>
    void addElement(T Obj) {
        getVector<T>().emplace_back(Obj.Name, Obj);
    }
private:
    std::tuple<std::vector<std::pair<std::string, Ts>>...> Vs;
};

using A = A_Impl<B, C>;

答案 3 :(得分:0)

如果您打算在一些地方使用向量,则可以专门设计一个模板来一次获得正确的向量,那么您的主模板代码可以是通用的:

class A{
public:

  template < typename T >
  void addElement(T obj)
  {
    getVector<T>().push_back(std::make_pair(obj.Name,obj));
  }

  template < typename T >
  T& getElement(size_t index)
  {
    return getVector<T>().at(index).second;
  }

private:
  vector<std::pair<std::string, B>> V1;
  vector<std::pair<std::string, C>> V2;

  template < typename T >
  vector<std::pair<std::string, T>>& getVector();
};

template <>
vector<std::pair<std::string, B>>& A::getVector<B>() { return V1; }
template <>
vector<std::pair<std::string, C>>& A::getVector<C>() { return V2; }

答案 4 :(得分:-1)

如果您这样使用共享的BaseClass怎么办? 您将必须为BaseClass创建一个通用接口,但是我不确定B和C在功能方面会有多少不同。

class BaseClass{
public:
    std::string Name;
};

class B : public BaseClass{
};
class C : public BaseClass{
};

class A{
public:
    std::vector<std::pair<std::string, std::unique_ptr<BaseClass>> > V1;
    std::vector<std::pair<std::string, std::unique_ptr<BaseClass>> > V2;

    template <class T>  void addElement(T Obj)
    {

        std::pair<std::string, std::unique_ptr<T>> AddedPair(Obj.Name, std::make_unique<T>(Obj));

        if (typeid(T) == typeid(B)) 
            V1.push_back(AddedPair);

        else if (typeid(T) == typeid(C)) 
            V2.push_back(AddedPair);
    }  
};

int main()
{
  A a;
  B b;
  C c;

  a.addElement<B>(b) ;//-> then element b is added to vector V1
  a.addElement<C>(c) ;//-> then element c is added to vector V2
}