C++ 绑定重载成员函数并作为参数传递
C++ binding overloaded member function and passing as parameter
我有一个 class,它有两个功能,可以启动和停止一组东西。由于这两个函数是相同的,除了它们最终分别在每个事物上调用启动或停止函数,我想重构代码,以便将代码的主体移动到通用函数,并且我的启动和停止集合调用这个传递一个额外的参数,这是他们必须调用以启动或停止的函数。
当然,网上有很多std::bind()
教程和示例,但我没有找到任何文章或question/answer这里涵盖所有以下内容我面临的具体限制:
- 被绑定的函数是一个使事情复杂化的成员;它也是非常量
- 被绑定的函数也被重载了,所以必须正确区分
- 大多数
std::bind()
示例使用 auto
但在这种情况下,我需要知道 std::bind()
中 return 的类型才能作为参数传递至 action_widgets()
- 有两个布尔值,
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()
的 return 不匹配,但我哪里出错了?
最后一两个警告:因为这是针对企业客户的,所以我仅限于 C++11 解决方案(尽管对他人有益的解决方案值得赞赏),而且,尽管我热衷于更简单的解决方案使用 lambda 的解决方案,我被同事认为这可能同样棘手,无论如何,从技术角度来看,我很想知道我做错了什么。
您可以将 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)`)
我认为这里不需要 lambda 或 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
函数,就像您手动编写它们一样,没有额外的运行时开销。由于模板参数要求正确类型的函数,编译器应该正确地找出使用哪个重载函数......
我有一个 class,它有两个功能,可以启动和停止一组东西。由于这两个函数是相同的,除了它们最终分别在每个事物上调用启动或停止函数,我想重构代码,以便将代码的主体移动到通用函数,并且我的启动和停止集合调用这个传递一个额外的参数,这是他们必须调用以启动或停止的函数。
当然,网上有很多std::bind()
教程和示例,但我没有找到任何文章或question/answer这里涵盖所有以下内容我面临的具体限制:
- 被绑定的函数是一个使事情复杂化的成员;它也是非常量
- 被绑定的函数也被重载了,所以必须正确区分
- 大多数
std::bind()
示例使用auto
但在这种情况下,我需要知道std::bind()
中 return 的类型才能作为参数传递至action_widgets()
- 有两个布尔值,
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()
的 return 不匹配,但我哪里出错了?
最后一两个警告:因为这是针对企业客户的,所以我仅限于 C++11 解决方案(尽管对他人有益的解决方案值得赞赏),而且,尽管我热衷于更简单的解决方案使用 lambda 的解决方案,我被同事认为这可能同样棘手,无论如何,从技术角度来看,我很想知道我做错了什么。
您可以将 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)`)
我认为这里不需要 lambda 或 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
函数,就像您手动编写它们一样,没有额外的运行时开销。由于模板参数要求正确类型的函数,编译器应该正确地找出使用哪个重载函数......