Search code examples
c++templatestemplate-templates

overload of template template function - explicit call


So, first consider the following where template parameters are known implicitly from the function arguments:

#include <iostream>
using namespace std;

class A {};
class B {};

template <class T1, class T2>
class C { 
    T1 a; 
    T2 b; 
};

template <class T1>
class D {
    T1 a;
};

template <template<class, class> class TC, class TA, class TB>
void foo(TC<TA, TB> c) {
    std::cout << "T<T,T>" << std::endl;
};

template <template<class> class TD, class TA>
void foo(TD<TA> d){
    std::cout << "T<T>" << std::endl;
};

int main() {
    C<A,B> c;
    D<A> d;

    foo(c);
    foo(d);
}

And output is as you'd expect:

T<T,T>
T<T>

However, what if I don't have an instance of class C and D so I need to explicitly call the correct overload? How would this be done? i.e., have a main() that consists of:

int main() {
    foo<C<A,B> >();
    foo<D<A> >();
}

I've experimented with a few overloads of foo() as shown below:

template <template<class, class> class TC>
void foo() {
    std::cout << "T<T,T>" << std::endl;
};

template <template<class> class TD>
void foo(){
    std::cout << "T<T>" << std::endl;
};

template <template<class, class> class TC, class TA, class TB>
void foo() {
    std::cout << "T<T,T>" << std::endl;
};

template <template<class> class TD, class TA>
void foo(){
    std::cout << "T<T>" << std::endl;
};

However, this (and all permutations I've been able to think of) simply results in a series of errors along the lines of the (abbreviated) output shown below

prog.cpp: In function 'int main()':
prog.cpp:44:18: error: no matching function for call to 'foo()'
     foo<C<A,B> >();
                  ^
prog.cpp:44:18: note: candidates are:
prog.cpp:19:6: note: template<template<class, class> class TC> void foo()
 void foo() {
      ^
prog.cpp:19:6: note:   template argument deduction/substitution failed:
prog.cpp:24:6: note: template<template<class> class TD> void foo()
 void foo(){
      ^
prog.cpp:24:6: note:   template argument deduction/substitution failed:

Is what I'm looking to do even allowable? If so, where am I messing up?

---- EDIT ----

So as apple apple pointed out if my main() is as follows:

int main() {
    foo<C, A, B>();
    foo<D, A>();
}

I get the output as expected.

However, my real-world case winds up being more complex. I'll expand a bit here. The legacy code has (hundreds) of typedefs defined in headers elsewhere along the lines of:

typedef C<A, B> type_117;
typedef D<A>    type_252;

The class I'm working on is templated and is instantiated with one of those typedefs as the templating argument. So something along the lines of:

template <class Type>
class Test
{
public:
   Test();
   SomeClass mSC;
}
Test::Test()
  : mSC(foo<Type>())
{
};

where Test was instantiated as

Test<type_117> aTest;

So I've been trying to figure out how to write foo() for this context. At the point I call foo() within my Test's initializer am I able to "decompose" it to produce the <C,A,B> form? Or have I hit a roadblock and need to rework some of the existing framework?


Solution

  • template<class T>struct tag_t{constexpr tag_t(){}};
    template<class T>constexpr tag_t<T> tag{};
    

    these are type tags. They can be passed to functions without an instance of the type.

    Template functions will deduce on them.

    template <template<class, class> class TC, class TA, class TB>
    void foo(tag_t<TC<TA, TB>>) {
      std::cout << "T<T,T>" << std::endl;
    };
    
    template <template<class> class TD, class TA>
    void foo(tag_t<TD<TA>>){
      std::cout << "T<T>" << std::endl;
    };
    

    at call site do foo(tag<type_117>) and bob, as they say, is your uncle.

    In C++98 (ick):

    template<class T>struct tag_t{};
    foo(tag_t<type_117>());