1

I have been trying to create a templated class(Test2) that takes 2 template arguments,Type1 and Type2. It is known that the second argument would also be a templated class that takes 2 template arguments(TypeA and TypeB).

Now, for constructing an object of Test2, I want the user to be able to use either of 2 types of constructors:

  1. One that takes objects of Type1 and Type2.
  2. One that takes objects of Type1, TypeA and TypeB.

I wrote the following code:

#include <iostream>

template<class TypeA, class TypeB>
struct Test
{
    TypeA t1obj;
    TypeB t2obj;
    Test(const TypeA& t1, const TypeB& t2)
        : t1obj(t1), t2obj(t2) {std::cout<<"Test::Type1, Type2\n";}
};


template<class Type1,
         template<typename TypeX, typename TypeY> class Type2 >
struct Test2
{
    Type1 t1obj;
    Type2<typename TypeX, typename TypeY> t2obj; //Line 17

    Test2(const Type1& t1,
          const Type2<typename TypeX, typename TypeY>& t2) //Line 20
        : t1obj(t1), t2obj(t2) { std::cout<<"Test2::Type1, Type2\n";}

    Test2(const Type1& t1,
          const TypeX& x,
          const TypeY& y)
        : t1obj(t1), t2obj(x,y) { std::cout<<"Test2::Type1, X, Y\n";}

};

int main()
{
    Test<int, char> obj1(1,'a');

    Test2<int, Test<int, char> > strangeobj1(10,obj1);
    Test2<int, Test<int, char> > strangeobj2(1,2,'b');

}

I have tried a lot but I get really absurd errors like:

wrong number of template arguments (1, should be 2) on Line 17 and 20.

Prasoon Saurav
  • 91,295
  • 49
  • 239
  • 345
Saurabh Manchanda
  • 1,115
  • 1
  • 9
  • 21

5 Answers5

6

It doesn't work like that. Test<int, char> is a full blown type, instead of a template. So you need type parameters

template<class Type1,
         class Type2 >
struct Test2
{
    Type1 t1obj;
    Type2 t2obj; //Line 17

    Test2(const Type1& t1,
          const Type2& t2) //Line 20
        : t1obj(t1), t2obj(t2) { std::cout<<"Test2::Type1, Type2\n";}

    Test2(const Type1& t1,
          const typename Type2::a_type& x,
          const typename Type2::b_type& y)
        : t1obj(t1), t2obj(x,y) { std::cout<<"Test2::Type1, X, Y\n";}

};

For getting TypeX and TypeY it's useful to export them so you can use them in Test2 as shown above.

template<class TypeA, class TypeB>
struct Test
{
    typedef TypeA a_type;
    typedef TypeB b_type;

    // and using them, to show their meaning
    a_type t1obj;
    b_type t2obj;

    Test(const a_type& t1, const b_type& t2)
        : t1obj(t1), t2obj(t2) {std::cout<<"Test::Type1, Type2\n";}
};

Be sure to read Where to put the "template" and "typename" on dependent names to understand why and when to use typename before type names like above.

Community
  • 1
  • 1
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • But, why doesn't template template arguments work here to catch the arguments provided to Type2?? Could you also explicate the reason for that error I mentioned? – Saurabh Manchanda Nov 21 '10 at 17:48
  • I guess C++0x variadic templates would be pretty useful here, `Test2` wouldn't have to know anything about the arguments needed by `Type2`'s constructor. – Ben Voigt Nov 21 '10 at 17:53
  • @Ben correct. We could write a partial specialization of `Test2` which will deduce the template arguments automagically, without relying on fixed named typedefs to get them. – Johannes Schaub - litb Nov 21 '10 at 17:56
  • 1
    @Saurabh it's like expecting a function pointer to give you the value of its arguments it is called with. That makes no sense, because you can call a function multiple times with different argument values. `void f(void(*pf)(int a)) { int i = a; /* this doesn't work for similar reasons */ }`. Just like you can instantiate a template with multiple argument values. The declaration of your template template parameter can equivalently be written as `template class Type2`. The inner parameter names have no meaning. – Johannes Schaub - litb Nov 21 '10 at 17:57
  • So, using type parameters is the only way out in this case? Nothing else comes to rescue? – Saurabh Manchanda Nov 21 '10 at 18:03
  • 1
    @Saurabh to the rescue for what? You haven't presented an ultimate goal. – Johannes Schaub - litb Nov 21 '10 at 18:07
  • @Saurabh: Template template arguments can't come with arguments. The template they're passed to provides the arguments. – Ben Voigt Nov 21 '10 at 18:13
  • @Johannes: The goal was to create Test2 that would provide 2 constructors to the users as mentioned in my question. – Saurabh Manchanda Nov 21 '10 at 18:14
  • @Saurabh: Are you now asking about the case where you can't change `Test` to add the typedefs? See Reinderien's answer, but note that the keyword `typename` needs to be taken out four times before it will be accepted by a compliant compiler. – Ben Voigt Nov 21 '10 at 18:15
  • @Ben: I got that from Schaub's comment. – Saurabh Manchanda Nov 21 '10 at 18:15
  • @Ben: Seen his post. That surely gives me another way to achieve the same, but to me, Schaub's solution is far better. – Saurabh Manchanda Nov 21 '10 at 18:20
  • @Johannes: Great answer. Hugh, the template god has spoken ;) – fredoverflow Nov 21 '10 at 18:39
  • @Saurabh. You're using `Test2 >` , not `Test2` . This is the different between a template-template and a full-blown type. – Aaron McDaid May 14 '11 at 00:39
1

There are several errors with this, but the main error seems to be that

Test2<int, Test<int, char> >

is not how you pass a template template parameter. This would be passed using

Test2<int, Test>

That is because Test is a template but Test<int, char> is a type (generated from that template.)

sbi
  • 219,715
  • 46
  • 258
  • 445
0

Type1 is a type, Type2 is a template. What exactly do you think TypeX and TypeY are defined? Inside the line template<typename TypeX, typename TypeY> class Type2 >, they are ignored.

fredoverflow
  • 256,549
  • 94
  • 388
  • 662
0

Here's one option:

#include <iostream>

template<class TypeA, class TypeB>
struct Test
{
    TypeA t1obj;
    TypeB t2obj;
    Test(const TypeA& t1, const TypeB& t2)
        : t1obj(t1), t2obj(t2) {std::cout<<"Test::Type1, Type2\n";}
};


template<class Type1, typename TypeX, typename TypeY,
         template <typename TypeXi, typename TypeYi> class Type2>
struct Test2
{
    Type1 t1obj;
    Type2<typename TypeX, typename TypeY> t2obj; //Line 17

    Test2(const Type1& t1,
          const Type2<typename TypeX, typename TypeY>& t2) //Line 20
        : t1obj(t1), t2obj(t2) { std::cout<<"Test2::Type1, Type2\n";}

    Test2(const Type1& t1,
          const TypeX& x,
          const TypeY& y)
        : t1obj(t1), t2obj(x,y) { std::cout<<"Test2::Type1, X, Y\n";}

};

int main()
{
    Test<int, char> obj1(1,'a');

    Test2<int, int, char, Test> strangeobj1(10,obj1);
    Test2<int, int, char, Test> strangeobj2(1,2,'b');

}
Reinderien
  • 11,755
  • 5
  • 49
  • 77
  • It's `Type2`, not `Type2`. `TypeX` and `TypeY` are not dependent names. – Ben Voigt Nov 21 '10 at 17:50
  • Perhaps. The compiler doesn't seem to care. – Reinderien Nov 21 '10 at 17:55
  • @Ben see http://stackoverflow.com/questions/4231502/typename-template-and-dependent-names . They are dependent unqualified names. That's why it's syntactically invalid. Please let me hear your opinion on that FAQ entry. – Johannes Schaub - litb Nov 21 '10 at 18:01
  • @Johannes: Um, yeah. Either way, they're not qualified by a template parameter, so `typename` is invalid. @Reinderien: Your compiler is not standard compliant. – Ben Voigt Nov 21 '10 at 18:06
  • @Johannes: FAQ looks good. Only improvement I could suggest is an explanation of why `typename` appears only once, always to the left, away from the type it modifies, while `template` appears in the middle of the scope chain and potentially multiple times. – Ben Voigt Nov 21 '10 at 18:10
0

Test<int, char> is not a match for template<typename TypeX, typename TypeY> class Type2

The first one is an instantiation of a template class, it does not accept any parameters. The second one is a template class pattern accepting two parameters.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720