Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type deduction of const references in templated functions

Below I have a template function named ProxyCall, which accepts an object, a member function and its arguments. It simply forwards the call to the member function.

I would like to be able to call the function without using template qualifiers (imagine tons of such calls with multiple arguments). The type deduction mostly works but the compilers (both msvc and gcc 4.9) barf when I try to pass const reference parameters as in the example.

#include <string>

struct Widget {
    void f(const std::string& s, bool b) {}
};


template<typename T, typename... Args>
void ProxyCall(T &obj, void(T::*method)(Args...), Args&&... args) {
    (obj.*method)(std::forward<Args>(args)...);
}


int main(int argc, char* argv[])
{
    Widget w;
    std::string s;

    ProxyCall<Widget, const std::string&, bool>(w, &Widget::f, s, true); // OK
    ProxyCall(w, &Widget::f, (const std::string&)s, true); // also OK

    ProxyCall(w, &Widget::f, s, true); // ERROR: template parameter is ambiguous
    return 0;
}

My question is: How can I modify the above code so that the compiler would automatically deduce the types without resorting to explicit template qualifiers or explicit casting. It seems this should be possible considering that the compiler already knows that exact arguments types from the signature of Widget::f.

like image 455
Alf Avatar asked Oct 19 '22 17:10

Alf


1 Answers

template<typename T, typename... Args>
void ProxyCall(T &obj, void(T::*method)(Args...), Args&&... args) {
    (obj.*method)(std::forward<Args>(args)...);
}

Args is deduced from both the second argument and the trailing arguments to ProxyCall.
In your third case, since s is not const, Args is deduced to [std::string&, bool] (recall the rules for reference collapsing and forwarding references). However, the member functions signature is clearly different. Thus two different types are deduced for the first type in Args, which leads to a deduction failure.

Instead, make both the parameter types and the arguments independent - and forward the object argument, for the sake of ref-qualifiers:

template<typename T, typename F, typename... Args>
void ProxyCall(T&& obj, F method, Args&&... args) {
    (std::forward<T>(obj).*method)(std::forward<Args>(args)...);
}
like image 123
Columbo Avatar answered Oct 23 '22 00:10

Columbo