为什么模板化函子作为值传递而不是转发引用

Why is templated functor passed as value and not forwarding reference

本文关键字:引用 转发 值传 为什么      更新时间:2023-10-16

在我们这里的讨论中,我正在玩传递函子。C++ STL 将函子作为值传递(见于 std::for_eachstd::find_ifstd::transform

所以宣布我的将是这样的。

template<typename F>
void call_me(F f)
{
    f();
}

现在,调用call_me(ftor{})可能会调用ftor的复制构造函数(它很可能会被复制省略,所以情况并非如此)。但ftor f{}; call_me(f);会导致复制。如果ftor有大量数据,则可能是个问题。

我们将通过将其作为常量引用(void call_me(const F& f))传递来改进它,以摆脱不需要的副本。只要ftor::operator() const就可以了。否则,对 call_me 的调用将导致编译错误(丢失const限定符)。

那么,为什么要打扰常量引用,只使用引用(void call_me(F& f))。这很好,但它不适用于call_me(ftor{})的第一种情况,因为将 r 值绑定到(非常量)l 值引用是无效的。

声明f为转发引用(void call_me(F&& f))似乎在所有情况下都有效。我相信,这要归功于引用折叠。

那么,为什么模板化函子没有作为 STL 函数中的转发引用传递呢?

为什么模板化函子没有作为 STL 函数中的 r 值引用传递?

首先,它们是转发引用,而不是右值引用。

也就是说,就像许多关于语言设计的"为什么"问题一样,答案可以很简单:因为还没有人提出这个改变(参见如何提交提案)。我怀疑传递到标准库算法中的大量函数对象要么是 lambda,要么是无状态的,要么是极其便宜的可复制的。此外,如果你有一个如此昂贵的对象,你总是可以把它变成一个廉价的可复制的,用于算法的目的:

call_me(std::ref(huge_object));

最后,其中一些算法依赖于将这些函数对象传递给其他帮助程序。如果算法只是假设函数对象是"自由"复制的,这使得编码变得容易。如果我们在这里引入右值的可能性,这增加了另一层需要处理的问题 - 我们只是传递参考吗?具有 ref 限定operator()的函数对象呢?

上述的某种组合可能解释了为什么,例如,它仍然是:

template <class InputIt, class UnaryPredicate>
InputIt find_if(InputIt, InputIt, UnaryPredicate );

而不是

template <class InputIt, class UnaryPredicate>
InputIt find_if(InputIt, InputIt, UnaryPredicate&& );