Here is 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 cannot figure out why one call works and one call fails.
EDIT : Changing the parameter pack to Args... from Args&&... solves the compiler problem. But, I would still like to know why it fails.
When a template parameter (or parameter pack) is used in two deduced contexts, deduction is done independently for each and the result must match. This makes things like
tricky to use at best, because
Argsis being deduced from both the member function signature and the subsequent pack of arguments; coupled with the special rules for forwarding references, you'll usually not be getting an exact match.Now, in this case, you should get an exact match, because
thisis a prvalue of typeA*, soArgswill be deduced as the non-reference typeA*according to the forwarding reference rules, and that happens to match the signature ofB::fn. Unfortunately, due to bug 56701, GCC 4.8 considersthisto have typeA* constand deducesArgsaccordingly (and MSVC apparently has the same bug), causing a mismatch.I recommend making
func's type a template parameter, side-stepping the double-deduction issue entirely.Alternatively, you can constrain
functo "pointer to member ofBof some sort":Both also have the benefit of handling pointer to
constmember functions properly, compared to the original.If you really want to constrain
func's type further, use two packs:Another possible workaround, for this case only, involves playing with
thisto try to remove the erroneous const-qualification;+thisworks with GCC 4.8 but not MSVC;&*thisworks with MSVC but not GCC 4.8;+&*thisseems to work with both but is getting into line-noise territory...