需要对结构容器中的字段进行总计

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 来解决这个问题。我们可以使用方法指针作为模板参数来模板化 lambda 变量:

template <int (foo::*T)()>
auto func = [](const auto init, const auto i){ return init + (i->*T)(); };

funcfunc 适当特化作为最后一个参数传递给 accumulate 将与写出 lambda 具有相同的效果:

accumulate(cbegin(foos), cend(foos), 0, func<&foo::r>)

Live Example


另一个基于相同模板化前提的替代方案,不需要 , 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>)

Live Example


这两个示例所需的特殊性已在 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)(); }