tags:

views:

88

answers:

5

Hi,

I have a method and two classes defined like this:

template<template<class X> class T>
void doSomething()
{  
    T<int> x;
}
template <class T>
class ClassWithOneArg
{
    T t;
};

template <class T1, class T2>
class ClassWithTwoArgs
{
    T1 t1;
    T2 t2;
};

I can now

doSomething<ClassWithOneArg>();

but I cannot

doSomething<ClassWithTwoArgs>();

However, I'd like to pass ClassWithTwoArgs to doSomething, where T2 = double.

The only method I found is to create

template <class T1>
class ClassWithTwoArgs_Child
    : public ClassWithTwoArgs<T1, double>
{
};

and then

doSomething<ClassWithTwoArgs_Child>();

This works, but in my concrete case all classes require a constructor argument and thus I have to create a constructor with this argument also in the _Child-class and pass it to the base which I really want to avoid.

Do you have an idea how to do that?

Thanks a lot!

A: 

This works with MSVC:

template<class T>
void doSomething()
{  
    T x;
}

// class definitions omitted...

void test() {
    doSomething<ClassWithOneArg<int> >();
    doSomething<ClassWIthTwoArgs<int, double> >();
}

I do not fully understand why you want to define the first parameter of your template template parameter to be int inside of doSomething. Looks like a "template smell" to me, since doSomething has to know a lot about its template template parameter.

Wouldn't it be cleaner to call doSomething the way i proposed? (But obviously i don't know the context of your calls).

Wolfgang Plaschg
I'm afraid that's not what I am looking for, because there might be other cases where I would also like to pass "ClassWithTwoArgs<T1,std::string>".
Philipp
You want that inside `doSomething` the first parameter will always be `int` but the second parameter should be defined when calling `doSomething`?
Wolfgang Plaschg
Note that i edited my answer
Wolfgang Plaschg
What's the problem with defining the first parameter to be `int` not inside of `doSomething` but when you call the function?
Wolfgang Plaschg
Oh boy, i love template questions :)
Wolfgang Plaschg
+1  A: 

There is no generic solution. Your best bet is

template<class T> 
void doSomething() 
{   
    T x; 
} 
template <class T> 
class ClassWithOneArg 
{ 
    T t; 
}; 

template <class T1, class T2 = double> 
class ClassWithTwoArgs 
{ 
    T1 t1; 
    T2 t2; 
}; 

int main(){
    doSomething<ClassWithOneArg<int>>(); 
    doSomething<ClassWithTwoArgs<int, double> >();
}
Chubsdad
+2  A: 

Indirection is a solution. Instead of a template template parameter you pass a "meta function" -- a function that maps one type to another in form of a struct with a nested class template:

struct mf1 {
  template<class Arg1>
  struct eval {
    typedef ClassTemplateWithOneArg<Arg1> type;
  };
};

template<class Arg2>
struct mf2 {
  template<class Arg1>
  struct eval {
    typedef ClassTemplateWithTwoArgs<Arg1,Arg2> type;
  };
};

template<class MetaFunc>
void do_something()
{
  typedef typename MetaFunc::template eval<int>::type clazztype;
  clazztype x;
}

void foo() {
  do_something<mf1>();
  do_something<mf2<double> >();
}

In C++0x this could be reduced to a "template typedef":

template<class Arg1>
using NewClassTemplate = ClassTemplateWithTwoArgs<Arg1,double>;

which allows you to pass NewClassTemplate as a template template argument which also accepts only one template parameter.

sellibitze
Note the comment from Philip (I'm afraid that's not what I am looking for, because there might be other cases where I would also like to pass "ClassWithTwoArgs<T1,std::string>".). This solution won't work as 'double' is hard coded
Chubsdad
@chubsdad, of course, you could parameterize the meta function. Check out how i turned mf2 into a template itself and how it's used.
sellibitze
thanks, this solved my question! It is not a problem to change "doSomething" but I did not want to have to define the Template-Argument ("int" in my example) from outside of "doSomething".
Philipp
+1  A: 

It seems that what you are after is similar to the rebinding of allocators (given an allocator, containers need to be able to produce an allocator for a different type - e.g std::list<int> might need a allocator<list_node<int> > from allocator<int>.

However, the class templates would have to be modified for this.

template<class T>
void doSomething(const T&)
{  
    typename T::template rebind_1st<int>::type x;
}

template <class T>
class ClassWithOneArg
{
    T t;
public:
    template <class U>
    struct rebind_1st { typedef ClassWithOneArg<U> type; };
};

template <class T1, class T2>
class ClassWithTwoArgs
{
    T1 t1;
    T2 t2;
public:
    template <class U>
    struct rebind_1st { typedef ClassWithTwoArgs<U, T2> type; };
};

int main()
{
    doSomething(ClassWithOneArg<char>());
    doSomething(ClassWithTwoArgs<char, double>() );
}
visitor
+1  A: 

Assuming you want declare template instantiations of the same class with a different type for the first template parameter, it appears a version of visitor's code is possible that doesn't require modifying original classes.

template <class T, class NewFirstArg>
struct rebind_1st;

template <template <class> class T, class Arg1, class NewFirstArg>
struct rebind_1st<T<Arg1>, NewFirstArg>
{
    typedef T<NewFirstArg> type;
};

template <template <class, class> class T, class Arg1, class Arg2, class NewFirstArg>
struct rebind_1st<T<Arg1, Arg2>, NewFirstArg>
{
    typedef T<NewFirstArg, Arg2> type;
};

template <class T>
void foo()
{
    typename rebind_1st<T, int>::type x;
    (void)x;
}

template <class T>
struct One{};

template <class T1, class T2>
struct Two{};

int main()
{
    foo<One<char> >();
    foo<Two<char, double> >();
}
UncleBens