Domanda

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.)

È stato utile?

Soluzione

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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top