简体   繁体   English

通过引用将函数对象传递给std算法

[英]Passing function objects into std algorithms by reference

Isn't it better to pass function objects into the STL algorithms by forwarding reference rather then by value? 通过转发引用而不是通过值将函数对象传递给STL算法不是更好吗? It would allow one to utilize ref-qualifiers of operator () of function objects passed. 它允许一个人使用传递的函数对象的operator ()的ref限定符。

There are a couple of questions about std::for_each algorithm on SO ( 1 , 2 ), which are considering a problem with changing of observable state of function object passed to std::for_each . 有一对夫妇约问题std::for_each上SO算法( 12 ),它正在考虑与传递给函数对象的观测状态的改变问题std::for_each

Passing functional objects by lvalue leference would solve the problem as a side effect even for those of algorithms, which can't return functional object (due to they should return, say, output iterator last value or something else). 通过左值传递传递函数对象会将问题解决为副作用,即使对于那些不能返回功能对象的算法(因为它们应该返回,比如输出迭代器或其他值)。

For example the algorithm std::for_each can be changed from (copied from libc++): 例如,可以从(从libc ++复制)更改算法std::for_each

template<typename _InputIterator, typename _Function>
_Function
for_each(_InputIterator __first, _InputIterator __last, _Function __f)
{
  for (; __first != __last; ++__first)
    __f(*__first);
  return _GLIBCXX_MOVE(__f);
}

to: 至:

template<typename _InputIterator, typename _Function>
_Function &&
for_each(_InputIterator __first, _InputIterator __last, _Function && __f)
{
  for (; __first != __last; ++__first)
    _GLIBCXX_FORWARD(_Function, __f)(*__first);
  return _GLIBCXX_FORWARD(_Function, __f);
}

or (if such breaking changing is allowed) std::for_each can return void without loss of functionality. 或者(如果允许这样的更改) std::for_each可以返回void而不会丢失功能。 Similar changes (change from passing by value to passing by forwarding reference and change all invocations to calling std::forward ed function object instead of just non-const-lvalue) are possible for all the rest <numeric> 's and <algorithm> 's algorithms. 对于所有其余的<numeric><algorithm>可以进行类似的更改(从传递值到传递转发引用并将所有调用更改为调用std::forward ed函数对象而不仅仅是非const-lvalue)的算法。

I know a partial workaround: is to pass object, wrapped by std::ref (or std::cref to enforce const this ), but there are issues with forwarding operator () cv-ref-qualifiers from wrapped functional object to wrapper's operator () . 我知道一个部分解决方法:是传递对象,由std::ref包装(或std::cref强制执行const this ),但是将包装函数对象的转发operator () cv-ref-qualifiers转发给包装器的operator ()

Another workaround is to explicitly specify reference argument type into alorithm's template parameter list, but currently Function parameter sadly always follows the InputIterator and OutputIterator parameters in the list: 另一种解决方法是将参数参数类型显式指定到alorithm的模板参数列表中,但是当前的Function参数总是遵循列表中的InputIteratorOutputIterator参数:

#include <iostream>
#include <list>
#include <algorithm>
#include <iterator>
#include <utility>

#include <cstdlib>

int main()
{
    std::list< int > l{{1, 2, 3, 4}};
    std::copy_n(std::cbegin(l), l.size(), std::ostream_iterator< int >(std::cout, " "));
    std::cout << std::endl;
    struct F
    {
        int state;
        void operator () (int i) { state += i; }
    } f{0};
    using I = std::list< int >::const_iterator;
    std::for_each< I, F && >(std::cbegin(l), std::cend(l), std::move(f));
    std::cout << f.state << std::endl;
    return EXIT_SUCCESS;
}

By the way the change would allow to pass a non-copyable and/or non-moveable function objects to the algorithms w/o wrapping them. 顺便说一下,改变将允许将不可复制和/或不可移动的函数对象传递给不包装它们的算法。

Isn't it better to pass function objects into the STL algorithms by forwarding reference rather then by value? 通过转发引用而不是通过值将函数对象传递给STL算法不是更好吗?

Yes, it would be better. 是的,会更好。 And it would be better if there were a requirement that the functor need not be CopyConstructible , CopyAssignable , MoveConstructible or MoveAssignable . 如果要求CopyConstructible函数不需要CopyConstructibleCopyAssignableMoveConstructibleMoveAssignableMoveConstructible MoveAssignable However the standard specifically says in 25.1: 但是标准在25.1中具体说明:

Note: Unless otherwise specified, algorithms that take function objects as arguments are permitted to copy those function objects freely. 注意:除非另有说明,否则允许将函数对象作为参数的算法自由复制这些函数对象。 Programmers for whom object identity is important should consider using a wrapper class that points to a noncopied implementation object such as reference_wrapper<T> (20.9.4), or some equivalent solution. 对象标识很重要的程序员应考虑使用指向非复制实现对象的包装类,例如reference_wrapper<T> (20.9.4)或某些等效解决方案。 — end note ] - 结束说明 ]

This issue was considered all the way back in 1998 as LWG 92 . 这个问题在1998年被视为LWG 92 And at that time the Note I quote above was added (the Note has since been modified as reference_wrapper<T> didn't exist at the time). 并且当时添加了上面引用的注释(由于当时不存在reference_wrapper<T>因此注释已被修改)。

This was a good resolution for vendors of the std::lib, and a good resolution for members of the committee who had the job of fixing the specification, but not so much for people such as yourself wanting to use stateful functors. 这对于s​​td :: lib的供应商来说是一个很好的解决方案,对于那些负责修改规范的委员会成员来说是一个很好的解决方案,但对于像你这样想要使用有状态函子的人来说则不是那么好。

And of course, at that time, forwarding references weren't available as a possible solution. 当然,在那个时候,转发引用并不是一种可能的解决方案。 Also at that time, it was common for std::implementations to pass the functor around by value within an algorithm, which would further destroy its state (as demonstrated in the description of LWG 92 . 同样在那时,std :: implementation通常算法中通过值传递仿函数,这将进一步破坏其状态(如LWG 92的描述中所示)。

You have correctly touched upon all of the points connected to this issue: 您已正确触及与此问题相关的所有要点:

  • Clients can use std::ref instead, but this won't respect reference-qualified functors. 客户端可以使用std::ref ,但这不会尊重引用限定的函子。

  • Clients can explicitly specify functor reference parameters, but this won't prohibit implementations from copying the functor within the algorithm. 客户端可以显式指定函子引用参数,但这不会禁止实现算法中复制仿函数。

  • Explicitly specifying functor reference parameters is extremely inconvenient for the client since they are always ordered last in the template parameter list. 明确指定仿函数引用参数对于客户端来说非常不方便,因为它们总是在模板参数列表中最后排序。

Fwiw, libc++ is the only std::implementation that was written which forbade itself from internally copying functors. Fwiw,libc ++是唯一编写的std :: implementation,它禁止内部复制仿函数。 Ie if you code up the LWG 92 example: 即如果您编写LWG 92示例代码:

#include <algorithm>
#include <iostream>
#include <list>
#include <numeric>

template <class C>
void
display(const C& c)
{
    std::cout << '{';
    if (!c.empty())
    {
        auto i = c.begin();
        std::cout << *i;
        for (++i; i != c.end(); ++i)
            std::cout << ", " << *i;
    }
    std::cout << '}' << '\n';
}

class Nth {    // function object that returns true for the nth element 
  private: 
    int nth;     // element to return true for 
    int count;   // element counter 
  public: 
    Nth (int n) : nth(n), count(0) { 
    } 
    bool operator() (int) { 
        return ++count == nth; 
    } 
};

int
main()
{
    std::list<int> coll(10);
    std::iota(coll.begin(), coll.end(), 0);
    display(coll);
    auto pos = std::remove_if(coll.begin(), coll.end(), Nth{3});
    coll.erase(pos, coll.end());
    display(coll);
}

The results today are: 今天的结果是:

libc++ 的libc ++

{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
{0, 1, 3, 4, 5, 6, 7, 8, 9}

g++ 克++

{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
{0, 1, 3, 4, 6, 7, 8, 9}

VS-2015 VS-2015

{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
{0, 1, 3, 4, 6, 7, 8, 9}

g++'s libstdc++ and VS-2015 are still copying Nth internal to remove_if just as described 18 years ago by Nico Josuttis. 正如18年前Nico Josuttis所描述的那样,g ++的libstdc ++和VS-2015 仍在Nth内部复制到remove_if

Changing the code to: 将代码更改为:

    Nth pred{3};
    auto pos = std::remove_if(coll.begin(), coll.end(), std::ref(pred));

does portably change the results to: 可以将结果可移植地更改为:

{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
{0, 1, 3, 4, 5, 6, 7, 8, 9}

Imho, this is just a run-time error waiting to happen to programmers not familiar with the long history of the std::lib. Imho,这只是一个运行时错误等待发生在不熟悉std :: lib的悠久历史的程序员身上。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 传递std向量作为参考:没有匹配的函数来调用 - Passing std vector as reference: no matching function to call 将std :: pair的const引用传递给函数 - Passing a const reference of std::pair to a function std :: transform()在函数参数中传递引用 - std::transform() passing reference in the function parameter 存储和传递std :: function的 - 按值或按引用? - Store and passing std::function's - by value or by reference? 将函数对象指针传递给接受指向std :: function的指针的函数 - passing function objects pointers to functions accepting a pointer to a std::function 将取自std :: map的std :: pair引用传递给接受std :: pair引用的函数 - Passing an std::pair reference taken from an std::map to a function that accepts an std::pair refererence C++ 通过引用 dll 中的函数传递 std::string - C++ Passing std::string by reference to function in dll 将 char 数组传递给需要 const std::string 引用的 function - Passing a char array to a function that expects a const std::string reference std::function 内部存储器组织和副本; 传递引用 vs 值 - std::function internal memory organization and copies; passing reference vs value 为什么通过引用传递从std :: function派生的对象会导致程序崩溃? - Why passing an object derived from std::function by reference crashes the program?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM