Reputation: 1204
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
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:
Use some form of type-erasing facility like Boost.Variant
or Boost.Any
, as suggested by Baum mit Augen in the comments.
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
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
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