在std :: accumulate中使用变异函数

时间:2014-11-07 16:16:32

标签: c++ language-lawyer c++03 c++-standard-library

有些情况

x += y;

更有效率
x = x + y;

(假设xy属于某种具有复杂重载运算符的类类型。当然,我可以用

进行折叠
X x;
BOOST_FOREACH(Y const &y, collection)
    x += y;

(我被旧编译器困在平台上)但我不知何故更喜欢std::accumulate。是否有可能使({或其他算法,但不是for_each)使用+=代替?

std::accumulate应该像

一样调用运算符
sum = op(sum, *iter);

但可以依靠它,也就是

X x(std::accumulate(collection.begin(),
                    collection.end(),
                    X(),
                    std::mem_fun_ref(&X::operator+=)));

工作?

2 个答案:

答案 0 :(得分:2)

就你的保证而言......

C ++ 03 [lib.accumulate]:

  

要求:T必须满足CopyConstructible(20.1.3)和Assignable(23.1)类型的要求。 binary_op不会引起副作用。

因此该功能不得有任何“副作用”。其定义如下:

C ++ 03 [intro.execution]:

  

访问由volatile lvalue(3.10)指定的对象,修改对象,调用库I / O函数或调用执行任何这些操作的函数都是副作用,是执行环境状态的变化。

强调我的。

请注意,在C ++ 11中,措辞稍有变化(第26.7.2节):

  

要求:T应满足CopyConstructible(表21)和CopyAssignable(表23)类型的要求。在[first,last]范围内,binary_op既不会修改元素也不会使迭代器或子范围无效。

但是,您可以创建一种类型的标记类型,无论您抛出什么内容都是空操作。

struct tag {
    template<typename T>
    tag& operator=(const T&) { return *this; } // not explicitly necessary
};

然后你创建一个在左侧接受该标签类型的仿函数和一个闭包来保持结果。

struct accumulator {
    int& x;
    accumulator(int& x): x(x) {}
    tag operator()(tag t, int y) {
        x += y;
        return t;
    }
};

然后正常使用..

std::accumulate(v.begin(), v.end(), tag(), accumulator(x));

它应该有效。虽然我认为此时std::for_each是更好的选择,或者可能是BOOST_FOREACH宏。

您也可以编写自己的算法,这样的算法就更少了:

template<typename It, typename T, typename Op>
T inplace_accumulate(It begin, It end, T init, Op op) {
    for(; begin != end; ++begin) {
        op(init, *begin);
    }
    return init;
}

然后你只需要用这个签名喂它常规累加器:

struct accumulator {
    template<typename T>
    void operator()(T& x, const T& y) const {
        x += y;
    }
};

// somewhere else
int x = inplace_accumulate(v.begin(), v.end(), 10, accumulator());

答案 1 :(得分:1)

是的,你可以信赖它。

您可以编写自己的丑陋的运算符,忽略总和,并希望编译器优化您创建的垃圾。

请不要。我怀疑是否有人想阅读该代码。

为什么不推出自己的算法?

template<class InputIt, class T>
T accumulate_fast(InputIt first, InputIt last, T init)
{
    for (; first != last; ++first) {
        init += *first;
    }
    return init;
}

这是通用的,不需要为每种类型单独的自定义二元运算符。