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.
The decltype type specifier yields the type of a specified expression. The decltype type specifier, together with the auto keyword, is useful primarily to developers who write template libraries. Use auto and decltype to declare a function template whose return type depends on the types of its template arguments.
'auto' lets you declare a variable with a particular type whereas decltype lets you extract the type from the variable so decltype is sort of an operator that evaluates the type of passed expression.
decltype is a compile time evaluation (like sizeof ), and so can only use the static type.
Another thing to consider is that decltype isn't really necessary unless you're writing library code, auto is nice for everyday programming if you want to make your code more concise, it's up for debate wether using as much auto as possible is good, but it's virtually necessary when working with unutterable types like ...
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>>());
}
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); });
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With