11

I want to unpack the parameter pack in func (see line A), but it doesnt work. How can I unpack inside func< > or modify Line A only?

#include <iostream>
using namespace std;

void func()
{
   cerr << "EMPTY" << endl;
}

template <class A, class ...B> void func()
{
   cerr << "A: "  << endl;
   func<B... >(); // line A
}


int main(void)
{
   func<int,int>();
   return 0;
}

An expected output :

A:
A:

edited: all of answers are very good. thanks alot

cppython
  • 1,209
  • 3
  • 20
  • 30

4 Answers4

16

Sometimes it's easier to unpack everything at once, instead of recursively. If you simply want a parameter pack for_each, you can use a variant of the braced-init-list expansion trick (Live demo at Coliru):

template <class A>
void process_one_type() {
    cerr << typeid(A).name() << ' ';
}

template <class ...B> void func()
{
    int _[] = {0, (process_one_type<B>(), 0)...};
    (void)_;
    cerr << '\n';
}
Community
  • 1
  • 1
Casey
  • 41,449
  • 7
  • 95
  • 125
  • 2
    how to understand this statement ? (void)_; . it is too fancy for me – cppython Jan 17 '14 at 20:08
  • 3
    @cppython It's casting the variable `_` to the type `void`, a statement expression that generates no code and typically only has the effect of telling the compiler not to warn about `_` being unused. This is necessary since the array `_` is used only for the side effects of its initialization. – Casey Jan 17 '14 at 20:11
  • please restore your original post. I am studying your first template function :) – cppython Jan 17 '14 at 20:20
  • @cppython You can see it develop in the [edit history](http://stackoverflow.com/posts/21194071/revisions), right up until I decided that it needed to die. – Casey Jan 17 '14 at 20:36
  • I still don't understand why you need (void)_; I removed it. it works as before. – cppython Jan 18 '14 at 02:38
  • @cppython It suppresses compiler warnings about `_` being unused. – Casey Jan 18 '14 at 02:43
  • It could also be simplified to just `(char []) {(process_one_type(), 0)...};` – dan Apr 01 '16 at 17:35
  • (may want to include 0 element though) – dan Apr 01 '16 at 17:59
  • @dinvlad `(char[]){ .... }` is the syntax for a C99 compound literal of type `char[]`. It's not valid C++ syntax. – Casey Apr 01 '16 at 21:03
  • Ok, it's probably good style to adhere to the strict standard. In case full portability is not required though, it can still be used if supported by the compiler (e.g. GCC) and built without '-pedantic'. – dan Apr 02 '16 at 01:15
10

By using func<B... >(); you are implying that func is a function template, but your previously defined func() is not.

You need to define a func() template that accepts zero template arguments. Here's a working example (on g++ 4.8.1):

#include <iostream>
using namespace std;

void func()
{
   cerr << "EMPTY" << endl;
}

template <class ... B>
typename std::enable_if<sizeof...(B) == 0>::type func()
{
}

template <class A, class ...B> void func()
{
   cerr << "A: "  << endl;
   func<B... >(); // line A
}


int main(void)
{
   func();           // This outputs EMPTY
   func<int,int>();  // This will not output EMPTY
   return 0;
}
hpsMouse
  • 2,004
  • 15
  • 20
  • this is the answer i am looking for but i still dont understand how it works. – cppython Jan 17 '14 at 08:13
  • Q1. typename std::enable_if::type. this is the first time, i saw something between template<> and function name. what is that? – cppython Jan 17 '14 at 08:15
  • 1
    It's the return type. I'm sure you have seen it before :-) – hansmaad Jan 17 '14 at 08:20
  • 1
    @cppython: It a SFINAE trick. If `sizeof...(B)` is `true`, `enable_if::type` will be `void`. Otherwise, `enable_if` will not have any member named `type`, and template argument substitution will fail. – hpsMouse Jan 17 '14 at 08:23
  • 1
    @cppython just search for 'C++ variadic templates', for 'c++ sfinae' and C++ 'enable_if' –  Jan 17 '14 at 08:30
  • Q2. why can't I use template<> void func() to replace your template part? – cppython Jan 17 '14 at 08:44
  • @cppython: Template cannot have an empty parameter list. And partial specialization is not available for function templates. – hpsMouse Jan 17 '14 at 08:48
  • i just try to put template<> void func(){} before your main function. it works. it seems it did replace the template function. why? – cppython Jan 17 '14 at 08:53
  • @cppython: Did you remove the `` function template? A `template<>` with nothing in the parameter list will be considered as a specialization. It will not compile if there's no corresponding template. – hpsMouse Jan 17 '14 at 09:03
  • Q3. after reading more about template, May I say that the template<> void func() is explicit specialization of template xxx with template argument deducted? – cppython Jan 17 '14 at 19:38
8

Try this:

template <class A> void func()
{
    cerr << "A: " << endl;
}

template <class A, class B, class ...C> void func()
{
    cerr << "A: " << endl;
    func<B, C...>(); // line A
}
hansmaad
  • 18,417
  • 9
  • 53
  • 94
2

Consider what the invocation of the recursive call func<B...>(); looks like when B... is empty. It's calling func<>(); but the definition of your attempted base case func() is not a template function, ie. you can't call it via func<>();

Since we don't have partial specialization for function templates yet, (hopefully it will be supported soon) one way to do it is to use a class template to do the partial specialization and use the function to simply delegate the work to the class template.

#include <iostream>

/* Forward declaration. */
template <typename... T>
struct FuncImpl;

/* Base case. */
template <>
struct FuncImpl<> {

  void operator()() const {
    std::cout << "Base case" << std::endl;
  }

};  // FuncImpl<>

/* Recursive case. */
template <typename First, typename... Rest>
struct FuncImpl<First, Rest...> {

  void operator()() const {
    std::cout << "Recursive case" << std::endl;
    FuncImpl<Rest...>()();
  }

};  // FuncImpl<First, Rest...>

/* Delegate function. */
template <typename... T>
void Func() {
  FuncImpl<T...>()();
}

int main() {
  Func<>();
  Func<int, double>();
}

Personally I think this solution is cleaner than other solutions such as tagged dispatching or SFINAE, despite the cruft around operator()s.

mpark
  • 7,574
  • 2
  • 16
  • 18