为什么序列操作算法谓词是通过复制传递的?

时间:2013-06-21 11:49:59

标签: c++ c++11 stl-algorithm

我想知道为什么仿函数通过副本传递给algorithm函数:

template <typename T> struct summatory
{
    summatory() : result(T()) {}

    void operator()(const T& value)
    { result += value; std::cout << value << "; ";};

    T result;
};

std::array<int, 10> a {{ 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 }};
summatory<int> sum;

std::cout << "\nThe summation of: ";
std::for_each(a.begin(), a.end(), sum);
std::cout << "is: " << sum.result;

我期待以下输出:

  

总和:1; 1; 2; 3; 5; 8; 13; 21; 34; 55;是:143

sum.result包含0,这是ctor中指定的默认值。实现所需行为的唯一方法是捕获for_each

的返回值
sum = std::for_each(a.begin(), a.end(), sum);
std::cout << "is: " << sum.result;

这种情况正在发生,因为仿函数通过副本传递给for_each而不是引用:

template< class InputIt, class UnaryFunction >
UnaryFunction for_each( InputIt first, InputIt last, UnaryFunction f );

因此外部仿函数保持不变,而内部仿函数(外部的副本)更新并在执行算法(live demo)后返回,因此结果被复制(或移动)<完成所有操作后再次强烈> 。


必须有充分的理由以这种方式完成工作,但我并没有真正意识到这个设计的基本原理,所以我的问题是:

  • 为什么序列操作算法的谓词是通过复制而不是引用传递的?
  • 在传递引​​用方法的前面提供了复制方法有哪些优点?

3 个答案:

答案 0 :(得分:7)

这主要是出于历史原因。在98年,当整个算法的东西成为标准参考文献时出现了各种各样的问题。最终通过C ++ 03及更高版本的核心和库DR解决了这个问题。理智的ref-wrappers和实际工作绑定也只到达TR1。

那些尝试使用algos与早期C ++ 98具有使用ref params或return的函数的人可以回想起所有类型的麻烦。自编的算法也容易遇到可怕的'参考'问题。

价值传递至少工作得很好,并且几乎没有产生很多问题 - 并且尽早提高了ref和cref以帮助你调整需要的位置。

答案 1 :(得分:5)

这纯粹是猜测,但......

...让我们假设它通过引用const来获取。这意味着所有成员必须是可变的,运算符必须是const。这感觉并不“正确”。

...让我们暂时假设它通过引用非const来实现。它会调用非const运算符,成员可以正常工作。但是如果你想传递一个特设对象怎么办?就像绑定操作的结果(甚至C ++ 98有 - 丑陋和简单 - 绑定工具)?或者类型本身只是做你需要的一切,之后你不需要对象,只想像for_each(b,e,my_functor());那样调用它?这不起作用,因为临时工具不能绑定到非const引用。

所以也许不是最好的,但是最不好的选择是按值获取,在过程中尽可能多地复制它(希望不经常)然后在完成后,从for_each返回它。这可以很好地处理你的summatory对象的相当低的复杂性,不需要添加像参考const方法那样可变的东西,也适用于临时对象。

但YMMV,委员会成员的可能性很大,而且我猜最终他们认为最有可能适合大多数用例的投票。

答案 2 :(得分:1)

也许这可能是一种解决方法。捕获仿函数作为参考并在lambda中调用它

std::for_each(a.begin(), a.end(), [&sum] (T& value) 
    {
        sum(value);   
    });

std::cout << "is: " << sum.result;