Quote:Original post by Timkin
So you're suggesting that
template < typename T, typename Bar < T2 > > class Foo{...};
is the correct way to go if you want to know (within Foo) what type Bar< > is instantiated with? Why?
It's one of the ways to gain access to that information. I mentioned it because it was already suggested twice, and it probably - unless there is some problem with embedding a typedef in the passed template - is the easiest solution:
template< typename T >struct templateHavingType {public: typedef T someType;};template< typename T1, typename T2 >struct templateAcceptingTemplate { T1 someData; T2 someTemplate; void useT2sInstatiatedType() { typename T2::someType someT2BasedData; // ... }};int main() { templateAcceptingTemplate< int, templateHavingType< float > > foobar; foobar.useT2sInstatiatedType(); return 0;}
Quote:How does this differ (at a template and instantiation level) from
template <typename T, template < typename T2 > class Bar<T2> > class Foo{...};
, from which I clearly know that Bar is a template class and that it is instantiated with a type aliased by T2.
The syntax isn't legal:
template< typename T, template < typename
T2 > class Bar< T2 > > class Foo{...};
The T2 in this case is simply a placeholder; it's only there to for the sake of the humans reading the code. The following two declarations are identical:
template< typename T1, template< typename T2 > class C > struct S1; template< typename T1, template< typename > class C > struct S2;
Since you're using a template, the compiler only needs to know that the template takes 1 argument. It doesn't care what that is or how it affects other arguments, therefore other arguments can't be dependent on it.
Quote:Certainly with
template <typename T2> class Bar{...};template <typename T, typename B> class Foo{...};Foo<int, Bar<std::string> > foo;
I have no reason (from within Foo) to believe that Bar is a templated type.
You're right, you don't. But placing a specialized typedef usage inside of Foo will
force the use of - at least a compatible - template argument. When you refactor to static polymorphism from dynamic polymorphism this is exactly what you're doing - you force an implementation on objects that are passed in. Objects that do not meet that criteria will halt the compiler. It's the difference between an assumption and a mandate.
Quote:But if I have template < typename T, template <typename T2 > class Bar < T2 > > class Foo{...};
then I know that my second template parameter is a templated type and that it is instantiated on a type T2. Presumably if I'm passing templated types, I care that they're templates and not just regular types (because I'm probably going to use Bar objects in a context where I need to know the type upon which they're instantiated).
Yes, and you get the same guarantee with an embedded typedef, since only objects with that typedef will allow the code to compile.
The other alternative I suggested was to explicitly pass T2 when instantiating Foo, like:
Foo< int, Bar, std::string > foo;
But that's really a non-solution that breaks encapsulation - it's also highly error-prone.
Quote:I'm asking this because I'm truly trying to understand the intricacies of templating better... if I'm wrong (or suggesting a poor usage), please point out why and explain.
Sorry if I came off a little brash earlier - didn't mean to; I was rushed in typing my response. I'm also trying to understand templates better.
:stylin: "Make games, not war.""...if you're doing this to learn then just study a modern C++ compiler's implementation." -snk_kid