views:

239

answers:

3

Basically, given a template class like this:

template< class Value > class Holder { };

I would like to be able to discover the type Value for a given Holder class. I thought that I would be able to make a simple metafunction that takes a template template argument, like this:

template< template< class Value > class Holder > class GetValue
{
    typedef Value Value;
};

And then extract out the Value type like this:

GetValue< Holder< int > >::Value value;

But instead I just get an error message pointing to the metafunction declaration:

error: ‘Value’ does not name a type

Is there any way to accomplish this kind of thing? Thanks.

[EDIT] I also get the error messages:

error: type/value mismatch at argument 1 in template parameter list for ‘template<template<class Value> class Holder> class GetValue’
error:   expected a class template, got ‘Holder<int>’

Which leads me to conclude that Phil Nash is right, you can't pass a class as a template template argument.

+1  A: 

I think you need a typename in there somewhere.

[edit] Damn, was just going to check my suspicion that it needed to be before the GetValue, but Mykola got there first :-)

The issue is that Value is a dependent type. The compiler doesn't know whether it is meant to name a type or a value, so you need to provide the hint.

[edit2]

Oops. There is a problem with trying to answer too fast. I missed the fact that you were trying to use a template template to achieve this. What that allows you to do is to pass a template in instead of a type. In this case you can pass a Holder. But that doesn't help you because you want to pass a type, Holder<int>.

Looks like Mykola realised this too and removed his answer.

You'll need to use Partial Template Specialization for this. Before I had a chance to complete my own example Sebastian has beaten me to it :-)

Phil Nash
you guys are too fast! i can't afford to make any typos here ;)but the typo is not the real problem.
drpepper
Are you sure? Can you update your original question to reflect your change to make sure we're on the same page?
Phil Nash
I think you are right, missing typename seems to be the issue. @drepper: If not, then post the updated code.
Naveen
@naveen I added a typedef but it didn't make a difference
drpepper
@drpepper typename, you mean? - in any case the real problem was the template template
Phil Nash
+6  A: 

Why don't you simply change your holder class to

template< class Value > class Holder {
    typedef Value value_type;

    value_type m_val; // member variable
};

In any method that consumes an object of type Holder< T > you can access the contained type like that:

template< class THolder >
void SomeMethod( THolder const& holder ) {
     typename THolder::value_type v = holder.m_val;
}

This approach follows the pattern all STL classes use, e.g., std::vector< int >::value_type is int.

I think you're trying to do partial template specialization:

template<class T>
class GetValue {
};

template<class Value>
class GetValue< Holder<Value> > {
public:
    typedef Value value_type;
};

In your code, you could then do the following:

template<class THolder>
void SomeMethod( THolder const& h ) {
    typename GetValue< THolder >::value_type v = h.m_v;
}

In general, I'd prefer the first solution though.

Sebastian
It's not always possible to do this. I've certainly needed the externalised form before
Phil Nash
I've added an external form using partial template specialization.
Sebastian
You're right of course. You can use the external form in boost style to define e.g. the value_type of an array using the same partial specialization approach.
Sebastian
Yep, that's what I had in mind.
Phil Nash
the partial template specialization would be the right approach for me. thanks!
drpepper
but so verbose :(
drpepper
@drpepper: That's how metaprogramming in C++ is. With the partial specialization it should also be possible to make it even more generic (as in your example): `template <class T, template <class> class Holder> struct GetType<Holder<T> >{ typedef T type; };`
UncleBens
+2  A: 
template <class T> void extract_type(Holder<T>)
{
    printf("%s\n", typeid(T).name());
}

I'm assuming though that you want to use that in a non-templated function. That's a bit more difficult:

template <class T> class GetValue;
template <class T> class GetValue<Holder<T> >
{
public:
    typedef T value_type;
};

The magic google words are "partial template specialization"

MSN