C++ 如果(first,last,p)不引用谓词,为什么std::find_?

C++ 如果(first,last,p)不引用谓词,为什么std::find_?,c++,pass-by-value,stl-algorithm,c++17,c++11,C++,Pass By Value,Stl Algorithm,C++17,C++11,我查看了的各种签名,注意到采用谓词函数的风格似乎通过值接受它: template< class InputIt, class UnaryPredicate > InputIt find_if( InputIt first, InputIt last, UnaryPredicate p ); template 输入查找如果(先输入,后输入, 一元谓词p); 如果我理解正确,则带有捕获变量的lambda会为引用或其数据的副本分配存储,因此“传递值”可能意味着

我查看了的各种签名,注意到采用谓词函数的风格似乎通过值接受它:

template< class InputIt, class UnaryPredicate >
InputIt find_if( InputIt first, InputIt last,
             UnaryPredicate p );
template
输入查找如果(先输入,后输入,
一元谓词p);
如果我理解正确,则带有捕获变量的lambda会为引用或其数据的副本分配存储,因此“传递值”可能意味着为调用复制捕获数据的副本

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

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

第二,我对传球的理解正确吗

第三,在这种情况下,是否有理由按值传递而不是按引用传递?更重要的是,是否存在一些足够模糊的语法(hello,universalreference),可以让编译器做任何它想得到最大性能的事情

上面的一元谓词是按值参数吗

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

除此之外,lambda表达式是prvalue。这意味着,在保证拷贝省略的情况下,
p
直接从lambda表达式初始化。将闭包或捕获的对象传递到函数中时,不会生成额外的副本(但是函数可能会在内部生成更多副本,尽管这并不常见)

如果谓词是通过引用传递的,则需要具体化临时对象。因此,对于lambda表达式,通过引用传递的开关不会获得任何结果

如果您有其他类型的谓词,它们可以扩展到复制,那么您可以将
std::reference_wrapper
传递到该谓词对象,以获得一个便宜的“句柄”。包装工的工作是正确的

这个定义大部分是历史性的,但现在用传递值来定义它已经不是什么问题了


为了解释为什么引用语义学会很糟糕,让我们试着用多年的时间来理解它。简单的左值引用不行,因为现在我们不支持绑定到右值。常量左值引用也不行,因为现在我们要求谓词不修改任何内部状态,为什么

所以到目前为止,我们真的没有其他选择。传递值比引用更好。根据新标准,我们可能会修改我们的方法。为了支持右值,我们可以添加右值引用重载。但这是一种冗余练习,因为它不需要做任何不同的事情


通过传递一个值,调用者可以选择如何创建它,对于prvalues,实际上是免费的。如果调用方愿意,它们可以显式地提供引用语义。因此,没有任何损失,我认为在使用和API设计的简单性方面获得了很多好处。

实际上有多种原因:

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

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

  • 与任何类型的引用参数(包括转发引用)不同,值参数可以被省略


  • 如果它要存储谓词的副本,那么通过引用传递可能不如通过值传递。但是你问的是传递还是存储?@juanchopanza我想问题是关于总体效率,所以你可以给出你的答案。可能是这样的重复,为什么不让它成为参考参数,为什么要强迫人们使用
    参考包装器
    ?有什么好处?@Yola-历史原因?鼓励人们使用“轻”谓词?你挑吧。不管怎么说,现在没有很好的理由来更改函数签名。@Yola历史原因和引用语义很糟糕。将“default”设置为值语义并强制用户执行一些特殊操作以获取引用语义是正确的方法。我正在研究
    find\u if
    的VS17实现,它使用具有以下签名的内部函数
    模板内联
    为什么它需要通过引用传递到下一个级别?@Yola-可能是因为它希望避免在内部复制更多副本。仅仅因为他们能,并不意味着他们想悲观。公共API发送关于引用语义的明确消息。内部API可以做任何它想做的事情。