需要对结构容器中的字段进行总计
Need to Total Fields From a Container of structs
这更像是一个代码清洁度问题,因为我这里已经有一个例子了。我在代码中做了大量的工作,所有这些 lambda(其中一些是相同的)的创建已经开始让我厌烦了。
所以给定结构:
struct foo {
int b() const { return _b; }
int a() const { return _a; }
int r() const { return _r; }
const int _b;
const int _a;
const int _r;
};
我有一个指向它们的指针容器,比方说 vector<foo*> foos
,现在我想遍历该容器并获取其中一个字段的总和。
例如,如果我想要字段 _r
,那么我目前的做法是:
accumulate(cbegin(foos), cend(foos), 0, [](const auto init, const auto i) { return init + i->r(); } )
我到处都在写这行。是否可以对此进行改进?我真的很想写这样的东西:
x(cbegin(foos), cend(foos), mem_fn(&foo::r));
我认为该标准没有提供类似的内容。我显然可以写它,但是它需要 reader 去找出我的可疑代码,而不是仅仅知道 accumulate
做了什么。
我建议写一个自定义仿函数生成器,而不是编写一个自定义的累加器,returns 一个可以用作 std::accumulate
.
的参数的仿函数
template<class Fun>
auto mem_accumulator(Fun member_function) {
return [=](auto init, auto i) {
return init + (i->*member_function)();
};
}
然后
accumulate(cbegin(foos), cend(foos), 0, mem_accumulator(&foo::r));
一些变化:
对于对象容器:
template<class MemFun>
auto mem_accumulator(MemFun member_function) {
return [=](auto init, auto i) {
return init + (i.*member_function)();
};
}
使用数据成员指针而不是函数:
template<class T>
auto mem_accumulator(T member_ptr) {
return [=](auto init, auto i) {
return init + i->*member_ptr;
};
}
// ...
accumulator(&foo::_r)
支持仿函数,而不是成员函数指针:
template<class Fun>
auto accumulator(Fun fun) {
return [=](auto init, auto i) {
return init + fun(i);
};
}
// ...
accumulator(std::mem_fun(&foo::r))
也许可以将这些变体中的一些(全部?)组合起来,以便使用某些 SFINAE 魔法自动选择,但这会增加复杂性。
实际上有一个非常优雅的方法可以使用 Variable Templates which were introduced in c++14 来解决这个问题。我们可以使用方法指针作为模板参数来模板化 lambda 变量:
template <int (foo::*T)()>
auto func = [](const auto init, const auto i){ return init + (i->*T)(); };
将 func
的 func
适当特化作为最后一个参数传递给 accumulate
将与写出 lambda 具有相同的效果:
accumulate(cbegin(foos), cend(foos), 0, func<&foo::r>)
另一个基于相同模板化前提的替代方案,不需要 c++14, is the templatized function :
template <int (foo::*T)()>
int func(const int init, const foo* i) { return init + (i->*T)(); }
也可以通过简单地传递方法指针来使用:
accumulate(cbegin(foos), cend(foos), 0, &func<&foo::r>)
这两个示例所需的特殊性已在 c++17 where we can use auto
for template parameter types: http://en.cppreference.com/w/cpp/language/auto 中删除 这将允许我们声明 func
以便 any class,不仅仅是 foo
:
template <auto T>
auto func(const auto init, const auto i) { return init + (i->*T)(); }
这更像是一个代码清洁度问题,因为我这里已经有一个例子了。我在代码中做了大量的工作,所有这些 lambda(其中一些是相同的)的创建已经开始让我厌烦了。
所以给定结构:
struct foo {
int b() const { return _b; }
int a() const { return _a; }
int r() const { return _r; }
const int _b;
const int _a;
const int _r;
};
我有一个指向它们的指针容器,比方说 vector<foo*> foos
,现在我想遍历该容器并获取其中一个字段的总和。
例如,如果我想要字段 _r
,那么我目前的做法是:
accumulate(cbegin(foos), cend(foos), 0, [](const auto init, const auto i) { return init + i->r(); } )
我到处都在写这行。是否可以对此进行改进?我真的很想写这样的东西:
x(cbegin(foos), cend(foos), mem_fn(&foo::r));
我认为该标准没有提供类似的内容。我显然可以写它,但是它需要 reader 去找出我的可疑代码,而不是仅仅知道 accumulate
做了什么。
我建议写一个自定义仿函数生成器,而不是编写一个自定义的累加器,returns 一个可以用作 std::accumulate
.
template<class Fun>
auto mem_accumulator(Fun member_function) {
return [=](auto init, auto i) {
return init + (i->*member_function)();
};
}
然后
accumulate(cbegin(foos), cend(foos), 0, mem_accumulator(&foo::r));
一些变化:
对于对象容器:
template<class MemFun>
auto mem_accumulator(MemFun member_function) {
return [=](auto init, auto i) {
return init + (i.*member_function)();
};
}
使用数据成员指针而不是函数:
template<class T>
auto mem_accumulator(T member_ptr) {
return [=](auto init, auto i) {
return init + i->*member_ptr;
};
}
// ...
accumulator(&foo::_r)
支持仿函数,而不是成员函数指针:
template<class Fun>
auto accumulator(Fun fun) {
return [=](auto init, auto i) {
return init + fun(i);
};
}
// ...
accumulator(std::mem_fun(&foo::r))
也许可以将这些变体中的一些(全部?)组合起来,以便使用某些 SFINAE 魔法自动选择,但这会增加复杂性。
实际上有一个非常优雅的方法可以使用 Variable Templates which were introduced in c++14 来解决这个问题。我们可以使用方法指针作为模板参数来模板化 lambda 变量:
template <int (foo::*T)()>
auto func = [](const auto init, const auto i){ return init + (i->*T)(); };
将 func
的 func
适当特化作为最后一个参数传递给 accumulate
将与写出 lambda 具有相同的效果:
accumulate(cbegin(foos), cend(foos), 0, func<&foo::r>)
另一个基于相同模板化前提的替代方案,不需要 c++14, is the templatized function
template <int (foo::*T)()>
int func(const int init, const foo* i) { return init + (i->*T)(); }
也可以通过简单地传递方法指针来使用:
accumulate(cbegin(foos), cend(foos), 0, &func<&foo::r>)
这两个示例所需的特殊性已在 c++17 where we can use auto
for template parameter types: http://en.cppreference.com/w/cpp/language/auto 中删除 这将允许我们声明 func
以便 any class,不仅仅是 foo
:
template <auto T>
auto func(const auto init, const auto i) { return init + (i->*T)(); }