Home > OS >  How to properly specify a recursive template class member function?
How to properly specify a recursive template class member function?

Time:12-27

I want to unpack a variadic template parameter pack. As the function requires access to private members of the object, I decided to write it as a member function. To my understanding, according to the standard, a template function has to be specified in the same namespace as the enclosing class, I tried to separate declaration and definition. As I result I only received lookup errors.

Below is a short recreation of what I'm trying to do:

class Container{
private: 

    //My first try
    template<typename... Ts>
    void foo();

    //Second try
    template<> foo();
    template<typename T, typename... Ts>
    void foo();
}

template<> void Container:foo(){}

template<typename T, typename... Ts>
void Container::foo(){
    foo<Ts...>();
}

What am I supposed to write instead of the commented parts, or is there a more general mistake in how I'm trying this?

I already looked at questions like recursive variadic template to print out the contents of a parameter pack, but none of them used a member function, so it didn't really help sadly.

Also, this should just do nothing in case of an empty parameter list. Which is why the following didn't work.

template<typename T, typename... Ts>
void foo(){
    if constexpr (sizeof...(Ts)){
        foo<Ts...>();
    }
}

About the error messages:

For try 1 -

Container::foo() does not match any template declaration

For try 2 -

explicit specialization in non-namespace scope class Container

CodePudding user response:

In C 20 you can use template lambda to extract the first template parameter like this:

class Container {
 private: 
  template<typename... Ts>
  void foo();
};

template<typename... Ts>
void Container::foo() { 
  if constexpr (sizeof...(Ts))
    [this]<typename /*First*/, typename... Rest> {
      foo<Rest...>();
    }.template operator()<Ts...>();
}

Demo.

But compared to using recursion, fold expression (which already given in other answers) seems to be a more efficient approach in your case.

CodePudding user response:

Your first try didn't work because the declaration template<typename... Ts> void foo(); has to be matched with a declaration that looks the same, not template<typename T, typename... Ts> void foo() { // ..., which has different template parameters.

In C 17, it is pretty simple to "do something" for each thing in a parameter pack using fold expressions:

class Container {
private:
    template<typename... Ts>
    void foo();
};

template<typename... Ts>
void Container::foo() {
    // Fold over comma which calls and discards the result of a lambda
    (([&]{
        // Use `Ts` here. For example:
        std::cout << typeid(Ts).name() << '\n';
    }()), ...);
}
  • Related