Why can't my code compile? (ideal forwarding and parameter packages)
Here's the program:
#include <memory>
struct A;
struct B {
void fn(A* ptr) {}
};
template<typename ...Args>
void foo(B* b, void (B::*func)(Args...), Args&&... args)
{
(b->*func)(std::forward<Args>(args)...);
}
struct A {
void bar() { B b; foo(&b, &B::fn, this); } // fails
};
int main()
{
A a;
B b;
foo(&b, &B::fn, &a); // passes
return 0;
}
Here is the compiler output:
foo.cpp: In member function 'void A::bar()':
foo.cpp:16:47: error: no matching function for call to 'foo(B*, void (B::*)(A*), A* const)'
void bar() { B b; foo(&b, &B::fn, this); } // fails
^
foo.cpp:16:47: note: candidate is:
foo.cpp:10:10: note: template<class ... Args> void foo(B*, void (B::*)(Args ...), Args&& ...)
void foo(B* b, void (B::*func)(Args...), Args&&... args)
^
foo.cpp:10:10: note: template argument deduction/substitution failed:
foo.cpp:16:47: note: inconsistent parameter pack deduction with 'A*' and 'A* const'
void bar() { B b; foo(&b, &B::fn, this); } // fails
I can't figure out why one call works and one call fails.
EDIT: Changing the parameter package to Args...
from Args&&...
solves the compiler issue. But I would still like to know why this fails.
source to share
When a template parameter (or parameter package) is used in two inferred contexts, the inference is performed independently for each and the result must match. It does things like
template<typename ...Args>
void foo(B* b, void (B::*func)(Args...), Args&&... args) { /* ... */ }
difficult to use at best because it Args
is inferred from both the signature of the member function and the subsequent package of arguments; coupled with special rules for link forwarding, you will usually not get an exact match.
Now, in this case, you should get an exact match because this
is a type value A*
, so it Args
will be inferred as an unreferenced type A*
according to the forwarding rule's reference, and that matches the signature B::fn
. Unfortunately, due to bug 56701 , GCC 4.8 thinks it this
has a type A* const
and outputs Args
accordingly (and MSVC apparently has the same error), causing the inconsistency.
I recommend doing the func
type of the template parameter, completely omitting the double inference problem.
template<typename PMF, typename ...Args>
void foo(B* b, PMF func, Args&&... args) { /* ... */ }
Alternatively, you can constrain func
to a "pointer to a member of B
some type":
template<typename F, typename ...Args>
void foo(B* b, F B::* func, Args&&... args) { /* ... */ }
Both have the advantage of properly handling member function pointers over the const
original.
If you really want to restrict the type func
further, use two packaging:
template<typename ...Args, typename... FArgs>
void foo(B* b, void (B::*func)(FArgs...), Args&&... args)
Another possible workaround, just for this case, involves playing with this
to try and remove the erroneous const qualification; +this
works with GCC 4.8 but not MSVC; &*this
works with MSVC, but not GCC 4.8; +&*this
seems to work with both, but falls into the area of ββline noise ...
source to share