Reputation: 1369
I'd like to make the following code work without changing the Child1
and Child2
classes at all:
#include <iostream>
int triple(int a) {
return a * 3;
}
int add(int a, int b) {
return a + b;
}
template<int (*F)(int)>
class Parent {
public:
Parent(int a) {
std::cout << "constructed: " << F(a) << std::endl;
}
};
class Child1 : Parent<triple> {
public:
Child1(int a) : Parent(a) {}
};
/*class Child2 : Parent<add> {
public:
Child2(int a, int b) : Parent(a, b) {}
};*/
int main() {
Child1 child(4);
//Child2 child(5, 6);
return 0;
}
As you can see for example, Child1
inherits from a Parent
that has been instantiated with the triple
function. Thus, when Child1
is instantiated with a four, it outputs "constructed: 12
".
In contrast, Child2
is commented out, as it obviously does not work yet. In the main function, I am trying to pass two arguments to the Child2
constructor, just like the underlying add()
function expects it. Yet, Parent
's constructor only accepts a single argument and will probably need template<typename Args...>
in front of it for the solution. Additionally, the Parent
class will need a template argument like int (*F)(Args...)
. Eventually, constructing a Child2
instance like the main function does shall output "constructed: 11
".
How can I accomplish this, i.e. create a template argument which is a function that can have any number of parameters? Again, please note that Parent
class's code is the only thing that may be changed.
Upvotes: 0
Views: 70
Reputation: 66200
It's too late to play?
I propose a variation of the auto
based C++17 VTT's answer that use class specialization to extract the Args...
input types (and the Ret
type, if useful).
I mean
template <auto>
class Parent;
template <typename Ret, typename ... Args, Ret(*F)(Args...)>
class Parent<F>
{
public:
Parent (Args const & ... as)
{ std::cout << "constructed: " << F(as...) << std::endl; }
};
The following is a full compiling example
#include <iostream>
int triple (int a)
{ return a * 3; }
long add (int a, int b)
{ return a + b; }
template <auto>
class Parent;
template <typename Ret, typename ... Args, Ret(*F)(Args...)>
class Parent<F>
{
public:
Parent (Args const & ... as)
{ std::cout << "constructed: " << F(as...) << std::endl; }
};
class Child1 : public Parent<triple>
{
public:
Child1 (int a) : Parent{a}
{ }
};
class Child2 : public Parent<add>
{
public:
Child2 (int a, int b) : Parent{a, b}
{ }
};
int main()
{
Child1 c1{4};
Child2 c2{5, 6};
}
Loose perfect forwarding but gain control over the number (and the types) of arguments.
Upvotes: 4
Reputation: 37549
With C++17 You can use deduced non-type template parameter and make constructor a variadic template:
template<auto x_pointer_to_function>
class Parent
{
public:
template<typename... x_Args>
Parent(x_Args &&... args)
{
std::cout << "constructed: " << ((*x_pointer_to_function)(::std::forward<x_Args>(args)...)) << std::endl;
}
};
Upvotes: 5