In the following code:
#include <iostream>
struct Base {
virtual ~Base() = default;
template <typename T, typename... Args> void helper (void (T::*)(Args..., int), Args...);
void bar (int n) {std::cout << "bar " << n << std::endl;}
};
struct Derived : Base {
void baz (double d, int n) {std::cout << "baz " << d << ' ' << n << std::endl;}
};
template <typename T, typename... Args>
void Base::helper (void (T::*f)(Args..., int), Args... args) {
// A bunch on lines here (hence the motivation for the helper function)
for (int n = 0; n < 5; n++)
(dynamic_cast<T*>(this)->*f)(args..., n);
// ...
}
int main() {
Base b;
Derived d;
b.helper(&Base::bar); // GCC 4.8.1 will accept this, Visual Studio 2013 won't.
d.helper<Derived, double>(&Derived::baz, 3.14); // Visual Studio 2013 will accept this, GCC 4.8.1 won't
}
I can't get either GCC4.8.1 or VS2013 to compile both lines above. They will compile only one but not the other (and they don't agree on which line is correct and incorrect either). The error message states template deduction failed by both compilers. So what's actually wrong? I've put all the template parameters in the last line (which I thought would be deducible), but it still cannot be deduced by GCC, though VS can. Yet VS cannot deduce the template arguments for the b.foo(&Base::bar);
line when I place the template arguments for that, yet GCC can deduce them without any template arguments. Totally bewildered here. Are both compilers bugged here? Any fix possible on the programmer's part?
A variadic function template can be called with any number of function arguments (the template arguments are deduced through template argument deduction ): template<class ...
To pass the variadic pack first we can use the following trick: wrap the function with the “technical” interface (variadic pack at the end) with another one that you can call with the “logical” interface (variadic pack at the beginning). We’ll proceed in three steps:
Args(optional) Function parameter pack (a form of declarator, appears in a function parameter list of a variadic function template) Parameter pack expansion (appears in a body of a variadic template) pattern ...
It would be understandable for coding guidelines to discourage the use of variadic templates except in special cases, until people are more familiar with them. Of course, you might need to call methods with just some of the parameters from the parameter pack, or some combination of parameter packs.
Parameter pack must be placed at the end of parameter list in order to be deduced automatically.
Compiler cannot deduce (Args..., int)
from given parameter list, use (int, Args...)
instead and the program will compile.
#include <iostream>
struct Base {
virtual ~Base() = default;
template <typename T, typename... Args> void helper (void (T::*)(int, Args...), Args...);
void bar (int n) {std::cout << "bar " << n << std::endl;}
};
struct Derived : Base {
void baz (int n, double d) {std::cout << "baz " << d << ' ' << n << std::endl;}
};
template <typename T, typename... Args>
void Base::helper (void (T::*f)(int, Args...), Args... args) {
// A bunch on lines here (hence the motivation for the helper function)
for (int n = 0; n < 5; n++)
(dynamic_cast<T*>(this)->*f)(n, args...);
// ...
}
int main() {
Base b;
Derived d;
b.helper(&Base::bar);
d.helper<Derived, double>(&Derived::baz, 3.14);
}
If you have to put int
at the end of parameter list, you can use identity
trick as @Barry said.
A barebone identity
implementation can be as simple as:
template<typename T>
struct identity {
typedef T type;
};
Then you can manually deduce parameter types:
template <typename T, typename... Args>
void Base::helper (void (T::*f)(typename identity<Args>::type..., int), typename identity<Args>::type... args) {
// A bunch on lines here (hence the motivation for the helper function)
for (int n = 0; n < 5; n++)
(dynamic_cast<T*>(this)->*f)(args..., n);
// ...
}
b.helper<Base>(&Base::bar);
d.helper<Derived, double>(&Derived::baz, 3.14);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With