我想知道为什么仿函数通过副本传递给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)后返回,因此结果被复制(或移动)<完成所有操作后再次强烈> 。
必须有充分的理由以这种方式完成工作,但我并没有真正意识到这个设计的基本原理,所以我的问题是:
答案 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;