【问题标题】:How to store any kind of function in a variable in C++?如何在 C++ 中的变量中存储任何类型的函数?
【发布时间】:2019-11-24 01:07:17
【问题描述】:

我将如何在变量中存储具有任意数量参数的任何类型的函数? 像这样的:

int add(int i, int j) {
return i + j;
}

template<typename Function, typename... Args>
class Class {
private:
    std::function<Function(Args...)> function;
public:
    Class(Function _function, Args... _args) {
        Now what?
    }
};

int main() {
    Class test(add, 1, 2);
    test.function();
}

【问题讨论】:

  • 您打算如何处理上述功能集合?你打算怎么称呼他们?
  • 就像我在 main 函数中演示的那样。
  • @user686368 这不是你真正想要的。我们正在尝试确定您是否真的在尝试解决此处的 XY 问题。
  • @user686368 我的意思更笼统。如果您真的只需要调用一个函数,只需编写add(1, 2)。你应该解释你的限制。
  • 这只是一个简单的例子来说明我的问题。该类将具有更多功能,而功能只是它的一个方面。

标签: c++ variadic-templates


【解决方案1】:

你可以试试这样的

#include <iostream>
#include <functional>
#include <tuple>

int add(int i, int j) {
    return i + j;
}

template<typename Function, typename... Args>
class Class {
private:
    Function function_;
    std::tuple<Args...> args;
public:
    Class(Function _function, Args... _args) :  
        function_ { std::forward<Function>(_function) } ,
        args{std::forward<Args>(_args)...}
    {}

    auto function()
    {
        return std::apply(function_,args);
    }
};

int main() {
    Class test(add, 1, 2);
    std::cout<< test.function() << std::endl ;
}

演示:https://wandbox.org/permlink/ZqFSSN2K5HU9HMRm

【讨论】:

  • 重塑 std::function!
  • 好吧,没想到绑定。我的解决方案的好处是他以后可能会更改参数。但我同意你的解决方案更好。
  • 我的解决方案可以像你的一样重新绑定
  • 需要直接初始化tuple,而不是_
【解决方案2】:

这就是bind 的用途!

您的Class 实际上只是std::bind。让我们假设您对它有进一步的用途,并使其包装std::bind 逻辑。所以我们需要存储一个不接受更多参数的std::function,并返回(在这种情况下)int……但我们将使它成为通用的(ReturnType)。

然后在初始化过程中,您“绑定”构造 Class 的参数,并将它们转发到 std::function(使用 std::forward 允许移动语义用于更复杂的参数类型)。

#include <functional>
#include <iostream>

int add(int i, int j)
{
    return i + j;
}

template <typename Function, typename... Args>
class Class
{
private:
    using ReturnType = std::invoke_result_t<Function, Args...>;
    std::function<ReturnType()> function;

public:
    Class(Function _function, Args... _args) 
        : function(std::bind(_function, std::forward<Args>(_args)...))
    {}

    auto Call()
    {
        return function();
    }
};

int main() {
    Class test(add, 1, 2);
    std::cout << test.Call() << '\n';
}

// Output: 3

(live demo)

或者,如果您不需要Class 做更多事情,那就是:

#include <functional>
#include <iostream>

int add(int i, int j)
{
    return i + j;
}

int main()
{
    auto func = std::bind(add, 1, 2);
    std::cout << func() << '\n';
}

// Output: 3

(live demo)


引导说明

您的 std::function&lt;Function(Args...)&gt; 类型是错误的,因为 Function 是整个函数的类型,但您将它用作返回类型。

【讨论】:

  • -1:永远不要在通用代码中使用绑定。如果参数是绑定表达式/占位符,那么事情就会变得非常疯狂,没有实现绑定的人可能无法理解。在初学者、中级或高级 C++ 代码中,无缘无故地出现爆炸性的极端情况是个坏主意;并且通过 bind 避免它是一个真正的痛苦。
  • @Yakk-AdamNevraumont 我还没有实现绑定,但我从来没有遇到过你提到的那种麻烦。你能举个例子吗?
  • @LightnessRacesinOrbit 如果将std::bind 的结果传递给std::bind 会发生什么? “如果存储的参数 arg 是类型 T ,其 std::is_bind_expression::value == true (例如,另一个绑定表达式直接传递到对 bind 的初始调用中),则 bind 执行函数组合”- - en.cppreference.com/w/cpp/utility/functional/bind -- 你上面的示例代码将通用参数传递给std::bind,这意味着占位符/组合代码开始起作用,并且可能会以荒谬的方式中断。
【解决方案3】:
template<class R>
struct Class {
  std::function<R()> function;
  template<class F, class...Args>
  Class( F&& f, Args&&...args ):
    function(
      [
        f=std::forward<F>(f),
        tup=std::make_tuple( std::forward<Args>(args)... )
      ]
      ()->R
      {
        return std::apply(f,tup);
      }
    )
  {} // ctor body
};
template<class F, class...Args>
Class( F, Args... )-> Class< std::invoke_result_t< F const&, Args const&... > >;

那里。

int main() {
  Class test(add, 1, 2);
  test.function();
}

请注意,我的 Class 类型仅取决于返回类型。

【讨论】:

    【解决方案4】:

    在 c++11 及更高版本中,您可以使用 Lambda 闭包。它们完全按照您的描述进行。

    例如:

    int a = 1; 
    int b = 2;
    int c = [a, b]{ return a + b; }
    

    等于:

    struct TestFunctor {
      int a;
      int b;
      TestFunctor(int a, int b):a(a), b(b){}
      int operator()(){return a+b;}
    };
    
    int a = 1; 
    int b = 2;
    int c = TestFunctor(a,b)();
    printf("%d\n", c);
    

    Lambda 可以转换为 std::function 并随时调用... 例如:https://wandbox.org/permlink/1RQUF3UUrsgRdJ42

    【讨论】:

    • 这不会编译。您正在存储结果,而不是实际的函子。而且由于问题是关于如何最好地存储东西...(尽管您可以从 lambda 初始化的 template &lt;CallableType&gt; Class { CallableType func; }; 和存储的 CallableType 是一个好主意!当你'已经进行了更正...)
    • 在源示例中 lambda 也没有存储到任何地方 :) 我的意思是:wandbox.org/permlink/SOVKItI2VNNA5FK6
    • 嗯,也许我描述得不好。主要思想是,如果您需要使用可变数量的参数调用某个函数 - 您可以使用从 lambda 初始化的函数。
    【解决方案5】:

    接受的答案不好,因为std::bind 可以完全替换为 lambda,使用 std::bind 在现代 c++ 中是老式的,并且可能会导致一些性能问题,因为std::function 的结构有点重。

    [ see Why use std::bind over lambdas in C++14? ]

    先介绍一下c++20std::bind_front,这个功能是为了替代std::bind

    int add(int i, int j, int k) {
        return i + j + k;
    }
    
    auto f1 = std::bind_front(add, 1);
    auto f2 = std::bind_front(add, 1, 2);
    auto f3 = std::bind_front(add, 1, 2, 3);
    // all will print '6'
    std::cout << f1(2, 3) << '\n';
    std::cout << f2(3) << '\n';
    std::cout << f3() << '\n';
    

    不要忘记包含&lt;functional&gt; 标头:)

    【讨论】:

      猜你喜欢
      • 2019-07-11
      • 1970-01-01
      • 1970-01-01
      • 2019-08-09
      • 2013-10-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多