Question

In this example:

template<typename T>
struct ConditionalValue
{
    typedef boost::optional<T> type;
};

template<typename T>
struct FindRootValueType
{
    typedef typename T::root_type type;
};

template<typename T>
struct FindRootValueType<typename ConditionalValue<T>::type>
{
    typedef typename ConditionalValue<T>::type type;
};

template<typename T>
struct Base
{
    typedef T value_type;
    typedef typename FindRootValueType<value_type>::type root_type;
    std::vector<value_type> data;
};

template<typename T>
struct A : public Base<typename ConditionalValue<T>::type>
{
};

template<typename T>
struct B : public Base<A<T>>
{
};

template<typename T>
struct C : public Base<B<T>>
{
};

// C<int>::value_type == B<int>
// C<int>::data == std::vector<B<int>>
// C<int>::data ~= std::vector<std::vector<std::vector<boost::optional<int>>>>
// C<int>::root_type == boost::optional<int>

ConditionalValue is a template alias that simply tries to give an alternate name to boost::optional (such that something else can be used to replace it), and FindRootValueType is a metafunction intended to walk up a chain of types that have definitions similar to those shown at the bottom until it stops at the boost::optional, and then simply returns that.

However as written this does not work (at least not in VS2008). To fix it, the specialization of FindRootValueType must explicitly use boost::optional<T>, not the typedef that should be equivalent to it. (And this defeats the goal of only specifying the underlying implementation type in one place.)

Is this a compiler bug or is this supposed to not work? Or am I just doing something wrong? Is there a better way to write it such that it works as expected?

I also tried reversing the logic thusly:

template<typename T>
struct FindRootValueType
{
    typedef T type;
};

// define Base here

template<typename T>
struct FindRootValueType<Base<T>>
{
    typedef typename T::root_type type;
};

// define A here

template<typename T>
struct FindRootValueType<A<T>>
{
    typedef T type;
};

// define B, C here (no specialisation)

But this doesn't work either (I think because specialisation doesn't follow base types, or possibly it's just the order of definition). I don't want to have to specialise for B, C etc specifically.

(BTW, in both of the above, "not working" means that compiler errors were produced that indicated that it was using the base definition of FindRootValueType, not the specialisation.)

Was it helpful?

Solution

Inspired by this answer to a related question link, I tried the following and it seemed to work:

template<typename T>
struct HasRootType
{
private:
    typedef char no;
    struct yes { no m[2]; };

    static T* make();
    template<typename U>
    static yes check(U*, typename U::root_type* = 0);
    static no check(...);

public:
    static bool const value = sizeof(check(make())) == sizeof(yes);
};

template<typename T, bool = HasRootType<T>::value>
struct FindRootValueType
{
    typedef typename T::root_type type;
};

template<typename T>
struct FindRootValueType<T, false>
{
    typedef T type;
};

// define Base, A, B, C, etc here

Thus redefining the problem to "walk up the types until you find one without a root_type, then return that". I'm still curious why typedef-based specialisation didn't seem to work, though.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top