【问题标题】:C++ object keeps templated function and args as members to call laterC++ 对象将模板化函数和 args 保留为稍后调用的成员
【发布时间】:2018-11-03 04:42:30
【问题描述】:

我有一个类Door 实现了一个方法LockCheck(),还有一个类Stove 实现了一个方法BurnerCheck()。我想要一个类House,它将Door::LockCheck 或Stove::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)

House 类“看起来”应该是什么样的?

到目前为止,

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_;
};

注意事项:

  • C++11

  • 我已经看过相关的 SO 问题,例如 this 和 this。

【问题讨论】:

  • 我会用std::function&lt;void ()&gt; safety_func_; 做类似House(std::function&lt;void ()&gt; func) : safety_func_(func) {} 之类的事情,并将其用作auto house = House([]() { stove.BurnerCheck(true, false); });。
  • @melpomene 很酷,谢谢,简洁明了。后续问题:对执行私有safety_func_ 可调用对象的house.check_safety() 的调用是否反映了stove 在用于实例化house 时的状态,还是反映了stove 的当前状态?
  • 我很确定它需要stove 的副本。如果您希望它通过引用使用stove,则需要[&amp;]() { ... }。

标签: c++ c++11 templates variadic-templates std-function


【解决方案1】:

您可以将所有参数存储为一个元组。然后,您可以通过将元组解包到函数参数中来调用 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

【讨论】:

    【解决方案2】:

    一些初步考虑。

    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) 
    

    你可以调用stove的BurnerCheck()方法为

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

    现在我建议的 House 类:一个具有 std::function&lt;void(void)&gt; 成员的类,在 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();
     }
    

    如果您对从 checked 方法返回的值感兴趣...我想您可以只使用 ReturnType 参数将 House 设为模板类并相应地调整类。

    【讨论】:

    • 这很漂亮,感谢您引导我完成并帮助我理解!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-17
    • 2013-09-24
    相关资源
    最近更新 更多