这更像是一个代码清晰度问题,因为我已经在这里有一个例子。我在代码中做了很多,所有这些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));
我不认为标准提供了类似的东西。我显然可以写它,但是它需要读者找出我可疑的代码,而不仅仅知道accumulate
做了什么。
答案 0 :(得分:1)
我建议编写一个自定义仿函数生成器,而不是编写自定义累积器,它返回一个可以用作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魔法自动选择,但这会增加复杂性。
答案 1 :(得分:1)
实际上有一种非常优雅的方法可以使用Variable Templates中引入的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,是模板化函数suggested by StoryTeller:
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中删除,我们可以使用auto
作为模板参数类型:http://en.cppreference.com/w/cpp/language/auto这样我们就可以声明func
它可以由任何类使用,而不仅仅是foo
:
template <auto T>
auto func(const auto init, const auto i) { return init + (i->*T)(); }