Variadic Template Recursion - Initialising

I've started learning generics, and am trying to create a templated function that will instantiate each given class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>

class ClassA
{
public:
	ClassA() { std::cout << "A Initialized." << std::endl; }
};

class ClassB
{
public:
	ClassB() { std::cout << "B Initialized." << std::endl; }
};

template <class T, class ... Classes>
void ProcessInstantiation()
{
	T instance = T();

	// If I comment out this line, it compiles fine. If I recurse, it errors.
	ProcessInstantiation<Classes...>(); 
}

void ProcessInstantiation()
{
	std::cout << "Finished." << std::endl;
}

template <class ... Classes>
void InstantiateClasses()
{
	// ... other non-recursive behaviour here

	ProcessInstantiation<Classes...>();
};


int main()
{
	InstantiateClasses<ClassA, ClassB>();
	return 0;
}


Errors:
'ProcessInstantiation': no matching overloaded function found
'void ProcessInstantiation(void)': could not deduce template argument for 'T'


If I comment out the recursive line, it compiles fine, ProcessInstantiation will be called once and I will get an instance of the first given class. Otherwise, it errors as above.

(I am aware my instance is being created and destroyed inside the function :P)
Last edited on
The right term for "templated function" is function template.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <cstdio>

template <typename T, typename... Args>
struct impl
{ 
  void process() const
  { 
    T();
    impl<Args...>{}.process(); 
  }
};

// template <> struct impl<>
template <typename T> struct impl<T> 
{ 
  // void process() const {};
  void process() const { T(); }; 
};

template <typename... Args>
void process_instantiation() 
{ 
  impl<Args...>{}.process(); 
}

struct foo { foo() { std::puts("foo"); } }; 

int main()
{
    process_instantiation<foo, foo, foo>();
}

(Didn't test the code)

However, if the goal is just to instantiate an object with each type in the parameter pack, you need not recurse at all:
1
2
template <typename... Ts> void impl(Ts... args) {}
template <typename... Ts> void process_instantiations() { impl(Ts()...); }


Or in C++17
template <typename... Ts> void process_instantiations() { ((void)Ts(), ...); }
Last edited on
Thank you. I do want to do this with recursion, then I will be able to apply what I've learnt in other cases as well.

That code doesn't compile, specifically template <> struct impl<> gives a couple errors:
'impl': too few template arguments
explicit specialization; 'impl' is not a specialization of a class template


I have a few questions, as I'm mainly interested in understanding:
1. What is wrong with my initial example? Why does it give those errors?
2. Why are you wrapping them in structs? Is this necessary?
3. What if I wanted to do this with member functions of a class? (I didn't ask this initially as I didn't think the solution would involve wrapping functions in structs)

Edit:
Okay so I played around some more from your answer and managed to get this to work (needs class definitions from OP):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
template<class T=void, class ... Classes>
void Instantiate()
{
    T();
    Instantiate<Classes...>();
}

template<>
void Instantiate() {}

template <class ... Classes>
void InstantiateClasses()
{
    Instantiate<Classes...>();
};

int main()
{
    InstantiateClasses<ClassA, ClassB>();
    return 0;
}


I'm leaving this question as open as I still don't understand exactly what's doing on though.
Last edited on
That code doesn't compile

Right, sorry. See the edited post above for one potential fix.

1. What is wrong with my initial example? Why does it give those errors

Consider the recursive call
ProcessInstantiation<Classes...>();
When Classes is an empty pack, the call expands to
ProcessInstantiation<>();
This is a call to a particular function template - the nontemplate function isn't an option. The only choice is the function template with one or more one argument that isn't provided explicitly and can't be deduced, hence the error.

2. Why are you wrapping them in structs? Is this necessary?

It's not strictly necessary in this case.

However, using class types allows us to work around the inability to partially specialize function templates, and avoids some surprising behavior (see: http://www.gotw.ca/publications/mill17.htm ) when full specializations are involved. A typical claim is that it's bad practice to specialize function templates.

In general, working with classes also offers the highly useful ability to pass around overload sets (using the term loosely), as discussed here
http://www.cplusplus.com/forum/general/244823/
Good rationale for a specific example (C++14's transparent operator function objects) is given by n3421:
https://wg21.link/n3421

3. What if I wanted to do this with member functions of a class?

I think I'm misunderstanding the question: doesn't my first example use member functions?
Last edited on
Topic archived. No new replies allowed.