Некоторые предварительные соображения.
1) Если вы пишете шаблон класса House
template <typename ReturnType, typename... Args>
class House {
// ...
House(Args... args, std::function<ReturnType(Args...)> func)
: input_args_(std::forward_as_tuple(args...)),
safety_func_(func) {}
, где аргумент методов проверки "неизвестен" (и, я полагаю,отличается от типа к типу) вы должны знать, что аргументы при определении объектов House
и у вас есть разные типы House
(один тип House
для проверок Door
, один тип House
для Stove
проверки и т. д.) и (до C ++ 17) вы не можете объявить House
объект просто как
auto house = House(burner_args, stove_check);
, но вам необходимо явно указать типы шаблонов;что-то вроде
House<void, bool, bool> house{burner_args, stove_check};
Предложение: вас не интересует ReturnType
методов проверки (и если вы можете его игнорировать), сделайте House
класс не-шаблона и сделайте конструктор шаблона с переменным числом элементовдля этого;что-то вроде
class House
{
public:
template <typename ... Args, typename F>
House (Args ... as, F f)
2) Если у вас есть шаблонная функция / метод / конструктор с некоторыми фиксированными аргументами и вариационным списком аргументов, поместите вариабельный список аргументов в last позициюТаким образом, компилятор может выводить список переменных типов из аргументов, и нет необходимости явно его использовать.
Таким образом, предыдущий конструктор становится чем-то вроде
template <typename F, typename ... Args>
House (F f, Args ... as)
3) Насколько я знаю, нет способа передать указатель на фактический метод функции o в переменную;поэтому нет
auto stove_check = stove.BurnerCheck;
и stove_check
в качестве аргумента конструктора House
.
Обычный способ, который я знаю для такого рода проблем, - передать объект (stove
)и указатель на метод BurnerCheck
, относящийся к классу, а не к объекту;что-то вроде
auto house { House(stove, &Stove::BurnerCheck, /* variadic args */) };
Теперь конструктор становится
template <typename T, typename M, typename ... Args>
House (T t, M m, Args ... as)
, и вы можете назвать BurnerCheck()
метод stove
как
(t.*m)(as...)
Теперь мой предложенныйHouse
class: класс с std::function<void(void)>
элементом, который инициализируется в конструкторе House
с лямбда-выражением, которое захватывает объект, метод указателя и аргументы.
И check_safety()
метод, который просто вызываетэтот член.
Что-то следующее
class House
{
private:
std::function<void(void)> fn;
public:
template <typename T, typename M, typename ... Args>
House (T t, M m, Args ... as) : fn{[=]{ (t.*m)(as...); }}
{ }
void check_safety ()
{ fn(); }
};
Ниже приведен полный рабочий пример
#include <iostream>
#include <functional>
struct Door
{ void LockCheck (int, long) const { std::cout << "Door" << std::endl; } };
struct Stove
{ void BurnerCheck (char) const { std::cout << "Stove" << std::endl; } };
class House
{
private:
std::function<void(void)> fn;
public:
template <typename T, typename M, typename ... Args>
House (T t, M m, Args ... as) : fn{[=]{ (t.*m)(as...); }}
{ }
void check_safety ()
{ fn(); }
};
int main ()
{
auto stove { Stove{} };
auto door { Door{} };
auto house1 { House{stove, &Stove::BurnerCheck, 'a'} };
auto house2 { House{door, &Door::LockCheck, 1, 2L} };
std::cout << "Some other stuff" << std::endl;
house1.check_safety();
house2.check_safety();
}
Если вас интересует значение, возвращаемое из проверенного метода... Полагаю, вы можете сделать House
классом шаблона только с параметром ReturnType
и соответственно настроить класс.