Samethings
Samethings

Reputation: 31

Using template struct in vector with deffrent types

Hello this my c++ source

template <typename T>
struct Key
{
    string Name;
    T Value;
};

struct Block
{
    string Name;
    vector <Key> Keys; // I got error here ... !!!!
};

int main()
{
    Block thisBlock;

    Key <bool> Key1;
    Key <string> Key2;

    // Set key 1
    Key1.Name = "Key1";
    Key1.Value = false;

    // Set key 2
    Key2.Name = "Key2";
    Key2.Value = "Hey";

    // Set block with all keys
    thisBlock.Name = "Block1";
    thisBlock.Keys.push_back(Key1);
    thisBlock.Keys.push_back(Key2);

    return 0;
}

please guide me about this error ! i know i have to use <> in vector <Key> but if i do this, my block keys is limit to that type only ! is there any way to fix this problem?

Error

argument list for class template "Key" is missing (in block struct (vector <key>>)

Upvotes: 1

Views: 689

Answers (2)

alexeykuzmin0
alexeykuzmin0

Reputation: 6440

Key<bool> and Key<string> are two different types, unrelated one to another, so, you cannot store both of them in one std::vector, just like you cannot create a std::vector storing both int and float.

To solve this issue, you could create a common base class for your keys, something like this:

class KeyBase {
public:
    virtual ~KeyBase() = default;               // virtual destructor to avoid memory leaks
    std::string GetName() const { return name; }// some common functions
    virtual std::string ToString() const = 0;   // and some function to be overriden
    std::string name;                           // and members

    KeyBase(std::string name_) : name(name_) {};
    KeyBase() = default;
};

template<class T>
class Key : public KeyBase {
public:
    std::string ToString() const override { ... } // implementation of virtual functions
    T value;
};

...
std::vector<std::unique_ptr<KeyBase>> vk;
vk.push_back(std::make_unique<Key<bool>>());
vk.push_back(std::make_unique<Key<string>>());
std::cout << vk[0]->GetName() << ' ' << vk[1]->GetName(); // Works
std::cout << vk[0]->value << ' ' << vk[1]->value; // Does not work as KeyBase has no value
std::cout << dynamic_cast<Key<bool>*>(vk[0].get())->value; // Works as vk[0] is now casted

std::unique_ptr used here is a smart pointer type - it's semantics are just like the pointer, but it deletes the storing object when is deleted, making memory management much easier. Also it's important here that the std::vector stores pointer to KeyBase, and not KeyBase. This is done to avoid object slicing.

Also, please use std::dynamic_cast with caution: it will return nullptr if you try to cast to an incorrect type.

UP: To set the value during the push_back operation, one way is to use the helper function and setting constructor:

template<typename T>
class Key : public KeyBase {
    ...
    Key(T val) : value(val) {}
    Key(T val, std::string name) : KeyBase(name), value(val) {}
}

template<typename T>
std::unique_ptr<Key<T>> createKey(T value) {
    return std::make_unique<Key<T>>(value);
}
template<typename T>
std::unique_ptr<Key<T>> createKey(T value, std::string name) {
    return std::make_unique<Key<T>>(value, name);
}

...
vk.push_back(createKey<bool>(false));
vk.push_back(createKey<string>("abc"));

Actually, with such approach you could even omit the type name, like this:

vk.push_back(createKey(1, "abc")); // creates Key<int> with value 1 and name "abc"

But be extra careful with such omission, for example, "abc" has type const char* and so createKey("abc") will create Key<const char*>.

Upvotes: 3

You
You

Reputation: 23794

If Block is to use a generic Key, you must make it a template as well:

template <typename T>
struct Block
{
    string Name;
    vector <Key <T>> Keys;
};

Addressing the more general problem, i.e. that you want to store objects of different type in the same vector, will require something like std::any or std::variant:

struct Block
{
    std::string Name;
    std::vector <std::variant<bool, std::string>> Keys;
};

For pre-C++17, the Boost libraries have boost::variant and boost::any which do the same thing.

Upvotes: 1

Related Questions