Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inconsistent parameter pack deduction with variadic templates

I have the following C++11 example where I have a call function that uses variadic templates to accept and call a generic class method:

#include <utility>

template <typename T, typename R, typename... Args>
R call(R (T::*fn)(Args...), T *t, Args&&... args) {
    return ((*t).*fn)(std::forward<Args>(args)...);    
}

class Calculator {
    public:
    int add(const int& a, const int& b) {
        return a + b;
    }
};

int main() {
    Calculator *calculator = new Calculator();
    int* a = new int(2);    
    int* b = new int(4);

    // compiles
    int res1 = calculator->add(*a, *b);    

    // does not compile!
    int res2 = call<Calculator>(&Calculator::add,calculator, *a, *b);    

    return 0;
}

As commented in the code, I'm not able to pass an int when the function accept const int, while in the direction method call I can. I get the following compile error:

error: no matching function for call to ‘call(int (Calculator::*)(const int&, const int&), Calculator*&, int&, int&)’
     int res2 = call<Calculator>(&Calculator::add,calculator, *a, *b);    
                                                                    ^

inconsistent parameter pack deduction with ‘const int&’ and ‘int&’
     int res2 = call<Calculator>(&Calculator::add,calculator, *a, *b);    
                                                                    ^

Does the C++ variadic template enforce stricter type checking when compared to regular execution? I'm using g++ 4.8.1 with C++ 11.

like image 326
jeffreyveon Avatar asked Nov 18 '14 12:11

jeffreyveon


1 Answers

The way you are calling your function template, the template parameter pack Args will be deduced from two sources:

  • The type of the pointer to member function - int (Calculator::*)(const int&, const int&)
  • The actual types of the arguments (*a, *b) you passed for the function parameter pack - int &, int &

For deduction to succeed, the result deduced must exactly match. They obviously don't.

This isn't new or special to variadic templates. You have the same issue if you try to do std::max(1, 1.5) - the compiler deduces int from one argument, double from the other, and deduction fails because the two conflict.

Simplest fix is probably taking two packs:

template <typename T, typename R, typename... Args1, typename... Args2>
R call(R (T::*fn)(Args1...), T *t, Args2&&... args) {
    return ((*t).*fn)(std::forward<Args2>(args)...);    
}
like image 188
T.C. Avatar answered Sep 27 '22 18:09

T.C.