C++ 对象将模板化函数和参数保留为稍后调用的成员

C++ object keeps templated function and args as members to call later

我有一个 class Door 实现了方法 LockCheck(),还有一个 class Stove 实现了方法 BurnerCheck()。我想要一个 class HouseDoor::LockCheckStove::BurnerCheck 作为构造函数参数以及给定函数的一组未知参数。 House 然后会存储函数及其参数,以便稍后可以调用它们。例如,

auto stove = Stove();
auto stove_check = stove.BurnerCheck;
auto burner_args = std::make_tuple<bool, bool>(true, false);
auto house = House(burner_args, stove_check);
// do some other stuff...
house.check_safety();  // internally calls stove.BurnerCheck(burner_args)

classHouse"looks"应该喜欢什么?

到目前为止,我有,

template <typename ReturnType, typename... Args>
class House {

public:

    House(Args... args, std::function<ReturnType(Args...)> func)
        : input_args_(std::forward_as_tuple(args...)),
          safety_func_(func) {}
    };

private:

    Args... input_args_;  // Is this the correct declaration?
    std::function<ReturnType(Args...)> safety_func_;
};

备注:

您可以将所有参数存储为一个元组。然后,您可以通过将元组解包为函数参数来调用 safety_func_。解包可以直接在 C++17 中使用 std::apply.

完成
template <typename ReturnType, typename... Args>
class House {
public:

    House(Args... args, std::function<ReturnType(Args...)> func)
        : input_args_(std::forward<Args>(args)...),
          safety_func_(func) {}

    // Require C++17
    ReturnType call_safety() {
        return std::apply(safety_func_, input_args_);
    }
private:

    std::tuple<Args...> input_args_;
    std::function<ReturnType(Args...)> safety_func_;
};

对于纯 C++11 元组解包解决方案,请参阅 this post

一些初步考虑。

1)如果写模板classHouse

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) {}

其中检查方法的参数是 "unknown"(而且,我想,类型不同)当你定义 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 并设为可变参数它的模板构造函数;像

class House
 {
   public:
      template <typename ... Args, typename F>
      House (Args ... as, F f) 

2) 如果你有一个模板 function/method/constructor 带有一些固定参数和可变参数列表,将可变参数列表放在 last 位置,所以编译器可以从参数中推导出可变类型列表,不需要显式显示它。

所以前面的构造函数变成了

  template <typename F, typename ... Args>
  House (F f, Args ... as) 

3) 据我所知,没有办法将指向实际方法的指针传递给函数 o 给变量;所以没有

auto stove_check = stove.BurnerCheck;

并且没有 stove_check 作为 House 构造函数的参数。

我知道的解决这类问题的常用方法是传递对象 (stove) 和指向 BurnerCheck 方法的指针,而不是对象 class ;像

auto house { House(stove, &Stove::BurnerCheck, /* variadic args */) };

现在构造器变为

  template <typename T, typename M, typename ... Args>
  House (T t, M m, Args ... as) 

你可以调用 stoveBurnerCheck() 方法作为

  (t.*m)(as...)

现在我建议 House class:一个 class 和一个 std::function<void(void)> 成员被初始化,在 House 构造函数中,有一个捕获的 lambda对象、指针方法和参数。

还有一个 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 模板 class,仅使用 ReturnType 参数并调整class 因此。