Reputation: 161
Could anyone explain why compilers (g++, visual c++) fail to deduce the template argument in this case?
struct MyClass
{
void Foo(int x)& {}
void Foo(int x)&& {}
};
template<typename T>
void CallFoo(void(T::*func)(int)&)
{
//create instance and call func
}
int main()
{
CallFoo(&MyClass::Foo); // Fails to deduce T
}
Why compilers cannot deduce T to MyClass? This happens only for methods overloaded by ref qualifiers. If a method is overloaded by const-ness or parameter types, everything works fine. It seems that only Clang can deduce T in this case.
Upvotes: 16
Views: 737
Reputation: 111
If you want your template to bind to different reference types, you need to use the universal reference
template<typename T>
void func(T&& arg)
{
other_func(std::forward<T>(arg));
}
That will bind to either lvalue or rvalue references. std::forward will make sure the appropriate reference is used in subsequent calls. I'm not sure how to fit the double ampersand into your code, but maybe just
template<typename T>
void CallFoo(void(T::*func)(int)&&)
Perhaps better would be
template<typename func_t>
void CallFoo(func_t && f)
{
call(std::forward<func_t>(f));
}
template<typename func_t>
void call(typename std::remove_reference<func_t> & f)
{
f();
}
template<typename func_t>
void call(typename std::remove_reference<func_t> && f)
{
f();
}
or whatever syntax you need to invoke a function pointer, maybe *f();
And if you want to pass arguments as well:
template<typename func_t, typename ... args_t>
void CallFoo(func_t && f, args_t && ... args)
{
call(std::forward<func_t>(f), std::forward<args_t>(args)...);
}
template<typename func_t, typename ... args_t>
void call(typename std::remove_reference<func_t> & f, args_t && ... args)
{
f(std::forward<args_t>(args)...);
}
template<typename func_t, typename ... args_t>
void call(typename std::remove_reference<func_t> && f, args_t && ... args)
{
f(std::forward<args_t>(args)...);
}
Upvotes: 0
Reputation: 4214
Summarizing discussion in the comments: the support for reference-qualified member functions as template arguments is a relatively new feature for some compilers. However, the latest versions of most compilers will compile such code.
For example:
#include <iostream>
struct MyClass
{
void Foo(int) const &
{
std::cout << "calling: void Foo(int) const &\n";
}
void Foo(int) const &&
{
std::cout << "calling: void Foo(int) const &&\n";
}
};
template<typename T>
void CallFoo_lvalue(void (T::*foo)(int) const &)
{
T temp;
(temp.*foo)(0);
}
template<typename T>
void CallFoo_rvalue(void (T::*foo)(int) const &&)
{
(T{}.*foo)(0);
}
int main()
{
CallFoo_lvalue(&MyClass::Foo);
CallFoo_rvalue(&MyClass::Foo);
}
Will compile with:
producing the following output:
calling: void Foo(int) const &
calling: void Foo(int) const &&
For those who are wondering what &
and &&
are for: here's the quote from @JustinTime:
Basically, & is the lvalue ref-qualifier, and && is the rvalue ref-qualifier (binds to temporary object); in his example, MyClass m; m.Foo(3); would call the top one, while MyClass{}.Foo(3); would call the bottom one. They act on the implicit object parameter; lvalue ref-qualifier binds to lvalue reference, and rvalue ref-qualifier binds to rvalue reference (functions that have neither take the parameter as lvalue reference, but let it bind to either). Note that they don't actually change *this's type.
Upvotes: 1