为什么std :: find_if(first,last,p)不能通过引用获取谓词?

时间:2018-02-15 09:34:50

标签: c++ pass-by-value stl-algorithm c++17

我正在查看std::find_if on cppreference.com,的各种签名,我注意到采用谓词函数的风格似乎是按值接受它:

template< class InputIt, class UnaryPredicate >
InputIt find_if( InputIt first, InputIt last,
             UnaryPredicate p );

如果我理解正确,带有捕获变量的lambdas会为其数据的引用或副本分配存储空间,因此大概是“按值传递”意味着会为调用复制捕获数据的副本。 / p>

另一方面,对于函数指针和其他直接可寻址的东西,如果函数指针是直接传递的,性能应该更好,而不是通过引用指针(指向指针)。

首先,这是正确的吗?上面的UnaryPredicate是否是一个按值参数?

第二,我对传递lambdas的理解是正确的吗?

第三,在这种情况下,是否有理由通过价值而非参考?更重要的是,是不是有一些足够模糊的语法(hello,通用引用)可以让编译器做任何想要获得最大性能的东西?

2 个答案:

答案 0 :(得分:10)

  

上面的UnaryPredicate是否是一个按值参数?

是的,这就是它在函数参数列表中所说的内容。它接受推导的值类型。

除此之外,lambda表达式是prvalues。含义的保证副本省略,p从lambda表达式直接初始化 。在将函数传递给函数时,没有额外的闭包副本或捕获的对象(函数可能会在内部制作更多副本,但这并不常见)。

如果谓词是通过引用传递的,则需要实现临时对象。因此对于lambda表达式,交换机无法通过引用获取任何内容。

如果您有其他类型的谓词,这些谓词可以扩展,那么您可以将std::reference_wrapper传递给该谓词对象,以获得便宜的“句柄”。包装器的operator()将做正确的事。

定义主要是历史性的,但现在用传递值来实现这个问题确实不是问题。

为了详细阐述为什么引用语义会很糟糕,让我们试着用它来度过这些年。一个简单的左值引用不会,因为现在我们不支持绑定到右值。 const左值引用也不会这样做,因为现在我们要求谓词不修改任何内部状态,为什么?

所以,我们真的没有其他选择。传递值优于参考值。有了新标准,我们可能会修改我们的方法。为了支持rvalues,我们可以添加一个rvalue引用重载。但这是一种冗余练习,因为它不需要做任何不同的事情。

通过传递一个值,调用者可以选择如何创建它,对于prvalues,在中,它几乎是免费的。如果调用者需要,他们可以明确地提供引用语义。因此,没有任何东西丢失,我认为在使用简单性和API设计方面获得了很多。

答案 1 :(得分:3)

实际上有多种原因:

  1. 您总是可以将推导出的值参数转换为使用引用语义而不是反之亦然:只需传递std::ref(x)而不是xstd::reference_wrapper<T>并不完全等同于传递引用,但特别是对于函数对象,它做了正确的事情。也就是说,按值传递泛型参数是更通用的方法。

  2. 通过引用传递(T&)不适用于临时或const个对象,T const&不适用于非const&对象唯一的选择是T&&(转发参考),它在C ++ 11之前并不存在,并且算法接口自从C ++ 98引入以来没有改变

  3. 与任何类型的参考参数(包括转发参考)不同,可以复制值参数。