I\'ve got rather silly question but I hope that you can help me with that.
I\'ve got class with multiple vectors, and this vectors have different storage types.
<This may be a use case for a Tagged Tuple library. It makes it possible to index containers by associated type. So code dealing with dozens of similar vector<std::pair<std::string, B>> V1;
fields becomes generic:
#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;
}
You might use "generic getter" for your vector:
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;
};
Changing your member might make sense to have std::tuple
directly.
and you might want to templatize the whole class:
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>;
If you are going to use your vectors in a few places you could specialise a template to get the correct vector once, then your main template code can be generic:
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; }
What if you make use of the shared BaseClass like this?? You will have to create a common interface for BaseClass, I am not sure how much B and C will differ in terms of functionality though.
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
}
Instead of having
template <class T> void addElement(T Obj);
Just overload the function instead. That would give you
void addElement(const B& Obj)
{
V1.push_back({Obj.Name, Obj});
}
void addElement(const C& Obj)
{
V2.push_back({Obj.Name, Obj});
}
This saves you all the syntax of specializing the template or needing C++17 and if constexpr to make the decision at compile time.
The reason
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);
}
Doesn't work is the code in each if block needs to be valid (even if it could never be reached), but it can't be because you would be adding a different type into the vectors. if constexpr
helps but I find overloading is just as much typing and makes the code non-backwards compatible.
That means you would either have to specialize the template like
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});
}
or using 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);
}