C ++ привязка перегруженной функции-члена и передача в качестве параметра - PullRequest
2 голосов
/ 01 мая 2019

У меня есть класс с двумя функциями, которые запускают и останавливают коллекцию вещей.Поскольку эти две функции идентичны, за исключением того, что они в конечном счете вызывают функцию запуска или остановки для каждой вещи соответственно, я хотел бы реорганизовать код так, чтобы я переместил тело кода в общую функцию, и мои коллекции начала и остановки вызывают эту передачу вдополнительный параметр, который является функцией, которую они должны вызывать для запуска или остановки.

Естественно, в Интернете есть множество std::bind() учебных пособий и примеров, но я не нашел здесь ни одной статьи или вопроса / ответа, которые бы охватывали все следующие конкретные ограничения, с которыми я сталкиваюсь:

  • Связываемая функция является членом, который усложняет вещи;она также неконстантна
  • Связываемая функция также перегружена, поэтому ее следует правильно различать
  • В большинстве std::bind() примеров используется auto, но в этом случае мне нужно знатьтип возврата из std::bind() для передачи в качестве параметра action_widgets()
  • Существует два типа bools, a & b, которые фактически являются постоянными и могут быть связаны с функцией, хотяЯ еще не сделал это здесь.Одна вещь за раз.

Вот пример того, чего я пытаюсь достичь:

#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 (хотя решения для пользы других приветствуются), а также, хотя я заинтересован в более простом решении, использующем лямбды, коллеги считают, что это может быть одинаково сложно и в любом случаес технической точки зрения я хотел бы знать, в чем я ошибся.

Ответы [ 2 ]

4 голосов
/ 01 мая 2019

Я не думаю, что здесь есть необходимость в лямбдах или 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);

живой пример здесь

В основном это просто заставит компилятор генерировать для вас исходные функции start_widgets и stop_widgets, как если бы вы написали их вручную, без дополнительных затрат времени выполнения. Поскольку аргумент шаблона запрашивает функцию правильного типа, компилятор должен правильно определить, какую из перегруженных функций использовать & hellip;

3 голосов
/ 01 мая 2019

Вы можете думать о 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)`)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...