12

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?

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
Danvil
  • 22,240
  • 19
  • 65
  • 88

2 Answers2

10

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
  • 134,909
  • 25
  • 265
  • 421
  • Well, you're still technically using a `typedef`. :-) But this is a clever solution. – In silico Sep 12 '10 at 19:53
  • 1
    @In silico: That's certainly not what OP means. he says "…in the Small class?" – Potatoswatter Sep 12 '10 at 19:55
  • @Potatoswatter: Ah, I was only looking at the question title itself. – In silico Sep 12 '10 at 19:57
  • It would be a single master template for each template arity, right? (`template` matches only templates that have a single parameter) But, it's a very clean solution, certainly. – James McNellis Sep 12 '10 at 19:57
  • @James: In C++03, a master template for each arity x each index. In C++0x, one master for everything. – Potatoswatter Sep 12 '10 at 19:58
  • You can match templates with two or more parameters with that same primary template's specializatons. Just add another that read like `template – Johannes Schaub - litb Sep 12 '10 at 20:02
  • @Johannes: right, of course :vP . Likewise with index selection, the sole master should be `get_type_argument< T, size_t >`. – Potatoswatter Sep 12 '10 at 20:06
  • @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 Sep 12 '10 at 20:16
  • @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 Sep 12 '10 at 20:46
  • @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 Sep 12 '10 at 21:51
  • 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 Sep 13 '10 at 02:14
7

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
  • 51,091
  • 10
  • 150
  • 143