Reputation: 7268
I'm trying to implement a C++ class with a value
field that can point to anything (a bit like in boost::any). Currently I do the following:
class MyClass {
void* value;
template<typename T>
Myclass(const &T v) {
value = (void*)(new T(v));
}
};
The problem is now to implement a getValue() operation that creates a copy of the inner value with the right type:
template<typename T>
T getValue() {
return *value;
}
Here it cannot work because I'm trying to unreference a void* pointer. I was wondering which cast (static_cast? dynamic_cast? other...) I should use such that *value is properly converted into a T object and an exception is thrown if value was not originally of this type?
Thanks
Upvotes: 1
Views: 486
Reputation: 279385
which cast (static_cast? dynamic_cast? other...) I should use such that *value is properly converted into a T object
If you must do this conversion, then you should use static_cast
, which in general is designed to (among other things) reverse any standard conversion. There's a standard conversion from any object pointer type to void*
, and your getter reverses it, so use the cast designed for that:
return *static_cast<T*>(value);
You should also either remove the C-style cast from your constructor, or replace that with a static_cast
too.
A reinterpret_cast
would also work, but is "overkill". In general you should use the cast that is as restrictive as possible while still performing the conversion you need.
and an exception is thrown if value was not originally of this type
You are out of luck there - C++ cannot in general tell what the original type of the object was, once you've cast the pointer to void*
. Your code relies on the caller to call getValue
with the correct type. For example, consider what happens if the original type was char
-- that's just one byte in C++, there is no room set aside for any type information that would allow the compiler to check the cast in getValue
.
dynamic_cast
does check types in some limited circumstances, but since your template is fully generic, those limited circumstances might not apply.
If you don't like this, you could change your class to store, in addition to the object pointer, a pointer to a type_info
object (resulting from a use of the typeid
operator). See the standard header <typeinfo>
. You could then compare the type_info
object for the type T
in the constructor, with the type_info
object for the type T
in getValue
, and throw if they don't match.
As you say, your class is intended to be a bit like boost::any
, and getValue
is like any_cast
. You could consult the source and documentation of that class to see the tricks needed to do what you want. If there were a straightforward way to do it, then boost::any
would be a straightforward class!
Upvotes: 1
Reputation: 7618
You can't. C++ doesn't provide that sort of mechanism, at least not directly, not for void*
. A void*
does not have any information that the computer would need to determine what it is, and attempting to "check" if it is a valid whatever-you-cast-it-to is impossible because there aren't particular flags for that.
There are options, though. The first is to use some kind of universal base class, similar to Java's Object, and derive all of your other classes from that. dynamic_cast
will now work the way you want (returning NULL
if the object is not a valid object of the class you casted it to).
Another is to simply keep track of what type of object it is yourself. That means augmenting the void*
with another value that tells you what you need to cast it to.
But really, neither of these things strike me as good ideas. I think there is almost-definitely some other aspect of your design that should be changed rather than using these. Using templates, as @EdS. suggests, is a very good option, for example.
Upvotes: 0
Reputation: 124770
You cannot dereference a void*
, it simply makes no sense. Why not make the class itself generic? Then you can have:
template<typename T>
class MyClass {
T* value;
MyClass(const T& v) {
value = new T(v);
}
T getValue() {
return *value;
}
};
Make sure to create a destructor which deallocates value
and also to follow The Rule of Three. You could also make a version of getValue
that returns a const T&
(const
reference to T
) to avoid the copy if one is not required.
Upvotes: 4