C ++绑定重载的成员函数并作为参数传递

问题描述 投票:2回答:2

我有一个有两个函数的类,它们可以启动和停止一组事物。由于这两个函数是相同的,除了它们最终分别在每个事物上调用一个启动或停止函数,我想重构代码,以便我将代码的主体移动到一般函数,我的开始和停止集合调用此传递一个额外的参数,它是他们必须调用以启动或停止的函数。

当然,网上有很多std::bind()教程和示例,但我没有在这里找到任何涵盖我面临的所有以下特定约束的文章或问题/答案:

  • 被约束的函数是使事情复杂化的成员;它也是非常数
  • 被绑定的函数也被重载,因此必须正确区分
  • 大多数std::bind()示例使用auto,但在这种情况下,我需要知道std::bind()返回的类型,以便作为参数传递给action_widgets()
  • 有两个bool,ab,它们实际上是恒定的并且可以绑定到函数中,虽然我还没有在这里做过。一心一意。

这是我想要实现的一个例子:

#include <string>
#include <vector>
#include <functional>

struct Processor {

    using WidgetActionFunction = bool(Processor::*)(const std::string&,
                                                    bool, bool);

    // Function wrapper 
    using WidgetActionWrapper = std::function<bool(Processor&,
                                                   const std::string&, bool, bool)>;

    // These functions in reality are tied heavily to the class and are quite
    // large. They cannot easily be made static or free.
    bool stop_widget(const std::string& key, bool a, bool b) { return true; }
    bool start_widget(const std::string& key, bool a, bool b) { return true; }

    // Just to make life difficult, there are some overloads, which we're not
    // interested in.
    bool stop_widget(int event, bool a, bool b) { return true; }
    bool start_widget(int event, bool a, bool b) { return true; }

    // I created this function because start_widgets() and stop_widgets() were
    // identical except that internally they call start_widget() and stop_widget()
    // respectively. I want the main body of the code to be here and for the
    // appropriate function to be passed in.
    void action_widgets(std::vector<std::string>& widgets,
            bool a, bool b, WidgetActionWrapper& func) {
        std::vector<std::string> valid_widgets;
        valid_widgets.reserve(widgets.size());
        for (const auto& widget : widgets) {
            if (func(*this, widget, a, b)) {  // This is where func() gets invoked.
                valid_widgets.push_back(widget);
            }
        }
        std::swap(widgets, valid_widgets);
    }

    void start_widgets(std::vector<std::string>& widgets, bool a, bool b) {
        WidgetActionWrapper func =
            std::bind(static_cast<WidgetActionFunction>(&Processor::start_widget),
                      this, std::placeholders::_1, a, b); // compilation fails here.
        action_widgets(widgets, a, b, func);
    }

    void stop_widgets(std::vector<std::string>& widgets, bool a, bool b) {
        // Very similar to start_widgets() but calls with bound stop_widget()
        // instead.
    }
};

int main()
{
    return 0;
}

编译时我收到以下错误:

error: conversion from ‘std::_Bind_helper<false, bool (Processor::*)(const std::basic_string<char>&, bool, bool), Processor* const, const std::_Placeholder<1>&, bool&, bool&>::type {aka std::_Bind<std::_Mem_fn<bool (Processor::*)(const std::basic_string<char>&, bool, bool)>(Processor*, std::_Placeholder<1>, bool, bool)>}’ to non-scalar type ‘Processor::WidgetActionFunctor {aka std::function<bool(Processor&, const std::basic_string<char>&, bool, bool)>}’ requested

很明显,我的函数包装别名不符合std::bind()返回的但是我哪里出错?

最后一两点警告:因为这是针对企业客户端的,我仅限于使用C ++ 11解决方案(尽管为了其他人的利益而受到赞赏),而且,虽然我热衷于使用lambdas的简单解决方案,但我让同事相信这可能同样棘手,无论如何,从技术角度来看,我很想知道自己错了什么。

c++ std-function stdbind
2个回答
3
投票

当你分配给std::bind时,你可以认为std::function取消了前几个参数。

例如,这个:

bool(Processor::*)(const std::string&, bool, bool);

// Which is this:
class Processor { bool f(const std::string&, bool, bool); }
decltype(&Processor::f)

被分配到std::function<bool(Processor&, const std::string&, bool, bool)>

当你将它绑定到Processor&(在你的情况下,*this,像std::bind(&Processor::f, *this)),它现在应该被分配给std::function<bool(const std::string&, bool, bool)>(因为bind摆脱了Processor&参数)。

这里有两个修复。不要绑定:

WidgetActionWrapper func =
    std::bind(static_cast<WidgetActionFunction>(&Processor::start_widget),
              *this, std::placeholders::_1, a, b); // compilation fails here.
// becomes
WidgetActionWrapper func = &Processor::start_widget;

或者在绑定后将WidgetActionWrapper更改为正确:

// *this and the two bool parameters have been bound, so you only need a string to call
using WidgetActionWrapper = std::function<bool(const std::string&)>;
// (And then `func(*this, widget, a, b)` to `func(widget)`)

4
投票

我不认为这里需要lambdas或std::bind,尤其不是std::function以及它将引入的所有开销。您可以简单地使用一个成员函数模板,该模板被赋予指向实际成员函数的指针,以便在每个小部件上调用作为模板参数,例如:

struct Processor
{
    bool stop_widget(const std::string& key, bool a, bool b) { return true; }
    bool start_widget(const std::string& key, bool a, bool b) { return true; }

    bool stop_widget(int event, bool a, bool b) { return true; }
    bool start_widget(int event, bool a, bool b) { return true; }

    template <bool (Processor::* func)(const std::string&, bool, bool)>
    void action_widgets(std::vector<std::string>& widgets, bool a, bool b)
    {
        std::vector<std::string> valid_widgets;
        valid_widgets.reserve(widgets.size());
        for (const auto& widget : widgets)
        {
            if ((this->*func)(widget, a, b))
            {
                valid_widgets.push_back(widget);
            }
        }
        std::swap(widgets, valid_widgets);
    }
};

然后

processor.action_widgets<&Processor::start_widget>(widgets, true, false);
processor.action_widgets<&Processor::stop_widget>(widgets, true, false);

live example here

这基本上只会让编译器为您生成原始的start_widgetsstop_widgets函数,就像您手动编写它们一样,没有额外的运行时开销。由于模板参数要求正确类型的函数,编译器应该正确地找出要使用哪个重载函数...

© www.soinside.com 2019 - 2024. All rights reserved.