views:

87

answers:

2

A simple example:

template<typename _X> // this template parameter should be usable outside!
struct Small {
   typedef _X X; // this is tedious!
   X foo;
};

template<typename SomeSmall>
struct Big {
   typedef typename SomeSmall::X X; // want to use X here!
   SomeSmall bar;
   X toe;
};

Is there a way to access the template parameter X of Small without using a typedef in the Small class?

+4  A: 

Yes, define a second "getter" template with partial specialization.

template< typename >
struct get_Small_X; // base template is incomplete, invalid

template< typename X > // only specializations exist
struct get_Small_X< Small< X > > {
    typedef X type;
};

Now instead of Small<X>::X you have typename get_Small_X< Small<X> >::type.

By the way, _X is a reserved identifier, so you shouldn't use it for anything. X_ is a better choice.


Advanced topic: template introspection.

While I'm thinking about it, you don't need to define this separately for every template. A single master template should do it.

This compiles in Comeau, I know there are rules about matching template template arguments but I think it's OK… template template arguments are forbidden from the master template in partial specialization.

template< typename >
struct get_first_type_argument;

template< template< typename > class T, typename X >
struct get_first_type_argument< T< X > > {
    typedef X type;
};

template< typename X >
struct simple;

get_first_type_argument< simple< int > >::type q = 5;

This only works with "unary" templates but could be adapted in C++0x for the general case.

Potatoswatter
Well, you're still technically using a `typedef`. :-) But this is a clever solution.
In silico
@In silico: That's certainly not what OP means. he says "…in the Small class?"
Potatoswatter
@Potatoswatter: Ah, I was only looking at the question title itself.
In silico
It would be a single master template for each template arity, right? (`template<typename>` matches only templates that have a single parameter) But, it's a very clean solution, certainly.
James McNellis
@James: In C++03, a master template for each arity x each index. In C++0x, one master for everything.
Potatoswatter
You can match templates with two or more parameters with that same primary template's specializatons. Just add another that read like `template<template<typename,typename> class T, typename X, typename Y> struct get_first_type_argument<T<X,Y> > { typedef X type; };` and so forth.
Johannes Schaub - litb
@Johannes: right, of course :vP . Likewise with index selection, the sole master should be `get_type_argument< T, size_t >`.
Potatoswatter
@Johannes: would it be conformant for an implementation to implement `tuple_element` in the general case, to allow this? I hate that C++0x specifies partial specializations. However, the user is allowed to specialize standard templates as they see fit. Edit: Nope, `array` is incompatible. Edit 2: Or is it? `array` doesn't match `template< typename ... > class`.
Potatoswatter
@Potatoswatter yep in the general case it is impossible. Like for `array` and `bitset` it won't work. See http://stackoverflow.com/questions/301203/extract-c-template-parameters/301274#301274
Johannes Schaub - litb
@Johannes: The general case partial specialization does not preclude the specific case of `array`. (There is no case for `bitset`, I guess there should be.) I just wrote the code and tried it :v) . The reason it would be nonconforming is that the user is allowed to add specializations for their own types (17.4.3.1; C++0x 17.6.3.2.1).
Potatoswatter
Hmm, but that kind of raises the question of whether any specialization could be lower in priority than the general case. Or equal. Or whether the general case could be contrived to be the bottom.
Potatoswatter
+2  A: 

Depending on what you're doing, template template parameters might be a better option:

// "typename X" is a template type parameter. It accepts a type.
// "template <typename> class SomeSmall" is a template template parameter.
// It accepts a template that accepts a single type parameter.
template<typename X, template <typename> class SomeSmall> 
struct Big {
   SomeSmall<X> bar; // We pass X to the SomeSmall template.
   X toe; // X is available to this template.
}; 

// Usage example:

template<typename X>
struct Small { 
   X foo; 
};

struct MyType {};

// The foo member in Small will be of type MyType.
Big<MyType, Small> big;
In silico
For my application this was indeed the best way to go.
Danvil