我想知道为什么函数是通过复制传递给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 );
因此,外部函子保持不变,而内部函子(它是外部函子的副本)会更新,并在执行算法(实时演示)后返回,因此在完成所有操作后,结果会再次被复制(或移动)。
这样做肯定有充分的理由,但我并没有真正意识到这个设计的基本原理,所以我的问题是:
- 为什么序列运算算法的谓词是通过复制而不是引用传递的
- 在通过引用的方法之前,通过复制的方法有什么优势
这主要是由于历史原因。在98年,当整个算法被纳入标准参考文献时,出现了各种各样的问题。这最终通过C++03及更高版本的核心和库DR得到了解决。此外,合理的ref包装器和实际工作的绑定仅在TR1中才到达。
那些在早期的C++98中尝试使用具有使用ref params或return的函数的算法的人可以回忆起各种各样的麻烦。自写算法也容易遇到可怕的"引用到引用"问题。
传递价值至少效果很好,几乎没有造成太多问题——boost在早期就有ref和cref来帮助你解决需要调整的地方。
这纯粹是猜测,但。。。
让我们暂时假设它通过引用const来实现。这意味着您的所有成员都必须是可变的,并且运算符必须是const。这感觉不太"对"。
让我们暂时假设它通过引用非常数来实现。它会调用一个非常数运算符,成员可以很好地处理。但是,如果你想传递一个特别的对象呢?就像绑定操作的结果一样(即使C++98也有丑陋而简单的绑定工具)?或者类型本身只做你需要的一切,然后你就不需要对象了,只想像for_each(b,e,my_functor());
一样调用它?这是行不通的,因为临时变量不能绑定到非常量引用。
因此,也许不是最好的,但最不坏的选择是按值取值,在过程中尽可能多地复制它(希望不要太频繁),然后在完成后,从for_each返回它。这可以很好地处理总结对象的较低复杂性,不需要添加诸如引用const方法之类的可变内容,也可以处理临时对象。
但是YMMV,委员会成员也很可能如此,我想最终是对他们认为最有可能适合大多数用例的内容进行了投票。
也许这是一个变通方法。捕获函子作为引用,并在lambda 中调用它
std::for_each(a.begin(), a.end(), [&sum] (T& value)
{
sum(value);
});
std::cout << "is: " << sum.result;