A follow up to my prior entry : http://cogito.jonesmz.com/2015/01/type-conversion-between-c-template.html
That entry detailed template meta-programming techniques to allow for two otherwise not related template classes to be converted from one to another. Sadly, that technique doesn't allow for the C++ type system to convert from, say, Foo<T>* to Foo<U>*. Despite having the appropriate conversion functions to construct new instances of Foo<T> out of Foo<U>, this doesn't give us the ability to treat an existing pointer to Foo<T> as a pointer to Foo<U>, the two classes might have completely different implementations, different v-tables, different memory layouts, and in general be incompatible. Thus, trying to convert a pointer to Foo<T> to a pointer to Foo<U> is impossible, as the C++ type system simply isn't designed to make that a possibility, no matter how many conversion functions you try to apply.
But there is one trick that can help in some situations, by having a template class inherit from a different instantiation of itself!
A quick example, which uses some template metaprogramming techniques.
template<typename T>boost::mpl::empty_base is simply a POD struct that contains nothing, making inheriting from it a non-issue.
class Foo : public boost::conditional< boost::is_const<T>::value
, boost::mpl::empty_base
, Foo<const T> >::type
{
}
Any instantiation of Foo, Foo<T> where T is non const will be legal, and the resulting class will contain all of the members of both instantiations (with the appropriate inheritance rules), just as if the public foo<const T> were instead bar<const T>.
Any instantiation of Foo, Foo<T> where T is const will be legal, but will inherit from boost::mpl::empty_base, which contains no data or member functions, and will not inherit from any other instantation of Foo.
This technique provides us with a mechanism to allow in-place conversion of pointers between the two template instantiation.
In other words
Foo<T> * bar = new Foo<T>();Is fully legal, because any Foo<T> IS-A Foo<const T> by virtue of inheritance!
Foo<const T> * baz = bar;
Of course, this doesn't then permit
Foo<const T> * bar = new Foo<T>();Without a cast, just like we normally need when going from a base-class to a derived-class.
Foo<T> * baz = bar; // WRONG
If you're willing to introduce a few specializations, we can get some much more interesting behavior, such as the non-const version delegating to the const-version's methods if and only if we have both.
template<typename T, typename = void>With that pattern, pointer conversion from non-const->const will work properly, and classes that use the non-const version of functions will delegate to the const version of functions
class Foo
{
bar function1(T) = 0;
void function2(baz) = 0;
}
template<typename T, typename boost::enable_if< boost::is_const<T>::value >::type >
class Foo
{
bar function1(T) = 0;
T function2(baz) = 0;
}
template<typename T, typename boost::disable_if< boost::is_const<T>::value >::type >
class Foo : public Foo<const T>
{
bar function1(T)
{ return Foo<const T>::function1(T); }
T function2(baz)
{ return Foo<const T>::function2(baz); }
}
No comments:
Post a Comment