Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variadic helper function with partial argument pack

In the following code:

#include <iostream>

struct Base {
    virtual ~Base() = default;
    template <typename T, typename... Args> void helper (void (T::*)(Args..., int), Args...);
    void bar (int n) {std::cout << "bar " << n << std::endl;}
};

struct Derived : Base {
    void baz (double d, int n) {std::cout << "baz " << d << ' ' << n << std::endl;}
};

template <typename T, typename... Args>
void Base::helper (void (T::*f)(Args..., int), Args... args) {
    // A bunch on lines here (hence the motivation for the helper function)
    for (int n = 0;  n < 5;  n++)
        (dynamic_cast<T*>(this)->*f)(args..., n);
    // ...
}

int main() {
    Base b;
    Derived d;
    b.helper(&Base::bar);  // GCC 4.8.1 will accept this, Visual Studio 2013 won't.
    d.helper<Derived, double>(&Derived::baz, 3.14);  // Visual Studio 2013 will accept this, GCC 4.8.1 won't
}

I can't get either GCC4.8.1 or VS2013 to compile both lines above. They will compile only one but not the other (and they don't agree on which line is correct and incorrect either). The error message states template deduction failed by both compilers. So what's actually wrong? I've put all the template parameters in the last line (which I thought would be deducible), but it still cannot be deduced by GCC, though VS can. Yet VS cannot deduce the template arguments for the b.foo(&Base::bar); line when I place the template arguments for that, yet GCC can deduce them without any template arguments. Totally bewildered here. Are both compilers bugged here? Any fix possible on the programmer's part?

like image 547
prestokeys Avatar asked Jan 16 '15 02:01

prestokeys


People also ask

How many function arguments can a variadic function template have?

A variadic function template can be called with any number of function arguments (the template arguments are deduced through template argument deduction ): template<class ...

How to pass a function to a variadic pack in JavaScript?

To pass the variadic pack first we can use the following trick: wrap the function with the “technical” interface (variadic pack at the end) with another one that you can call with the “logical” interface (variadic pack at the beginning). We’ll proceed in three steps:

What is the difference between function parameter pack and expansion?

Args(optional) Function parameter pack (a form of declarator, appears in a function parameter list of a variadic function template) Parameter pack expansion (appears in a body of a variadic template) pattern ...

Is it okay to use Variadic templates?

It would be understandable for coding guidelines to discourage the use of variadic templates except in special cases, until people are more familiar with them. Of course, you might need to call methods with just some of the parameters from the parameter pack, or some combination of parameter packs.


1 Answers

Parameter pack must be placed at the end of parameter list in order to be deduced automatically.

Compiler cannot deduce (Args..., int) from given parameter list, use (int, Args...) instead and the program will compile.

#include <iostream>

struct Base {
    virtual ~Base() = default;
    template <typename T, typename... Args> void helper (void (T::*)(int, Args...), Args...);
    void bar (int n) {std::cout << "bar " << n << std::endl;}
};

struct Derived : Base {
    void baz (int n, double d) {std::cout << "baz " << d << ' ' << n << std::endl;}
};

template <typename T, typename... Args>
void Base::helper (void (T::*f)(int, Args...), Args... args) {
    // A bunch on lines here (hence the motivation for the helper function)
    for (int n = 0;  n < 5;  n++)
        (dynamic_cast<T*>(this)->*f)(n, args...);
    // ...
}

int main() {
    Base b;
    Derived d;
    b.helper(&Base::bar);
    d.helper<Derived, double>(&Derived::baz, 3.14);
}

If you have to put int at the end of parameter list, you can use identity trick as @Barry said.

A barebone identity implementation can be as simple as:

template<typename T>
struct identity {
    typedef T type;
};

Then you can manually deduce parameter types:

template <typename T, typename... Args>
void Base::helper (void (T::*f)(typename identity<Args>::type..., int), typename identity<Args>::type... args) {
    // A bunch on lines here (hence the motivation for the helper function)
    for (int n = 0;  n < 5;  n++)
        (dynamic_cast<T*>(this)->*f)(args..., n);
    // ...
}

b.helper<Base>(&Base::bar);
d.helper<Derived, double>(&Derived::baz, 3.14);
like image 150
Windoze Avatar answered Sep 28 '22 16:09

Windoze