Simon Goodman
Simon Goodman

Reputation: 1204

storing and re-using decltype value?

if I have a template:

template <class T>
struct Item
{
  T _value;
};

I can then do:

// ...
Item<int> x = { 42 }; // declared as an int

// ...
decltype(x._value) y = 20; // 'y' is also an int

But is it possible to store the decltype to a variable so it can be used later?

Why?
I want to store the values of items as pointer.

Something like std::vector<Item*> but as they are templates I have to store them as pointers to void:

std::vector<void*> is;
is.push_back(new Item<int>());
is.push_back(new Item<double>());
is.push_back(new Item<float>());

And this is all fine, but when it comes time to delete the pointer I need to re-cast my void* back to the proper type (so the destructors are called):

delete (Item<int>*)is[0];

And if I know the type, I could do:

delete (Item<decltype(whatever)>*)is[0];

Hence the reason I would need to store the decltype.

I hope this makes sense.

Upvotes: 12

Views: 2501

Answers (3)

Vittorio Romeo
Vittorio Romeo

Reputation: 93364

decltype is a language feature that allows you to retrieve a type at compile-time. It seems that you want to "store" that type so that you can correctly delete objects allocated on the dynamic storage at run-time. Assuming that's the case, decltype is not going to help here.

You have various options:

  1. Use some form of type-erasing facility like Boost.Variant or Boost.Any, as suggested by Baum mit Augen in the comments.

  2. Make your objects part of a polymorphic hierarchy and use smart pointers:

    struct ItemBase 
    {
        virtual ~ItemBase() { }
    };
    
    template <class T>
    struct Item : ItemBase
    {
        T _value;
    };
    
    int main() 
    {
        std::vector<std::unique_ptr<ItemBase>> items;
        items.emplace_back(std::make_unique<Item<int>>());
        items.emplace_back(std::make_unique<Item<float>>());                     
        items.emplace_back(std::make_unique<Item<double>>());
    }
    

Upvotes: 8

skypjack
skypjack

Reputation: 50548

If the problem is only to delete them, you can use unique_ptr with a custom deleter instead of naked pointers.
You don't need to modify your hierarchy to do this.
As an example:

std::vector<std::unique_ptr<void, void(*)(void*)>> is;
is.push_back(std::unique_ptr<void, void(*)(void*)>{new Item<int>(), [](void *ptr) { delete static_cast<Item<int>*>(ptr); }}); 

Even better if using emplace_back instead of push_back:

std::vector<std::unique_ptr<void, void(*)(void*)>> is;
is.emplace_back(new Item<int>(), [](void *ptr) { delete static_cast<Item<int>*>(ptr); }); 

It follows a minimal, working example based on the OP's code:

#include<vector>
#include<memory>

template<typename>
struct Item {};

int main() {
    using Deleter = void(*)(void*);
    std::vector<std::unique_ptr<void, Deleter>> is;
    is.emplace_back(new Item<int>(), [](void *ptr) { delete static_cast<Item<int>*>(ptr); }); 
    is.emplace_back(new Item<double>(), [](void *ptr) { delete static_cast<Item<double>*>(ptr); }); 
    is.emplace_back(new Item<float>(), [](void *ptr) { delete static_cast<Item<float>*>(ptr); }); 
}

Upvotes: 5

Jarod42
Jarod42

Reputation: 218098

You may store the deleter

so with std::shared_ptr, it becomes:

std::vector<std::shared_ptr<void>> items;

// The simplest
items.push_back(std::make_shared<Item<int>>(/*args...*/));

// Explicitly specify the (default) deleter
items.push_back(std::shared_ptr<void>{new Item<double>(/*args...*/),
                                      std::default_delete<Item<double>>{}});

// Explicitly specify the (default) allocator
items.push_back(
    std::allocate_shared<Item<float>>(std::allocator<Item<float>>{} /*, args...*/);

Upvotes: 1

Related Questions