通过参考

时间:2016-07-14 03:33:25

标签: c++ templates c++11

std::for_each接受并按值返回仿函数:

template< class InputIt, class UnaryFunction >
UnaryFunction for_each( InputIt first, InputIt last, UnaryFunction f );

虽然可以移动仿函数并将其移出,但我很感兴趣是否根本不涉及对象构造。如果我这样宣布自己的my_for_each

template< class InputIt, class UnaryFunction >
UnaryFunction&& my_for_each( InputIt first, InputIt last, UnaryFunction&& f);

my_for_each内,使用f调用std::forward<UnaryFunction>(f)(...),我可以避免移动构建参数的成本,并且作为奖励可以尊重ref-qualifiers。但我不确定应该归还什么。如果我这样做:

return std::forward<UnaryFunction>(f);

可以发生坏事(例如,悬空引用)吗?

(当我在this post设计for_each时会出现此问题。)

2 个答案:

答案 0 :(得分:8)

正如另一个正确答案所指出的那样,传递if (self.onApproveTap)//check if block not equal nil self.onApproveTap(@"Name"); !self.onApproveTap?:self.onApproveTap(@"name");//the same but with syntax sugar 和右值参考参数是危险的,因为参考生命周期扩展不会通勤。

当您想要传递转发参考const&时,返回的正确信息是T&&。这会将右值变为临时值,并将左值作为参考。

所以:

T

临时template< class InputIt, class UnaryFunction > UnaryFunction my_for_each( InputIt first, InputIt last, UnaryFunction&& f); 创建(移动到)副本。

如果您存储此副本,它将被省略到存储空间,因此零额外费用。除非您不存储它,并且移动费用昂贵,否则这基本上是最佳的。

答案 1 :(得分:4)

糟糕的事情可能会发生。

struct my_functor {
  void operator()(int x) { sum += x; }
  int sum = 0;
}

std::vector<int> v{1,2,3};

const auto& s = my_for_each(v.begin(), v.end(), my_functor{});
auto sum = s.sum;

此代码是未定义的行为。您构造了my_functor的临时实例。当函数调用my_for_each进入时,这将绑定到右值引用,延长其生命周期。当函数退出时,它将返回临时值的右值引用。但是,当函数退出时,临时最初绑定的rvalue引用将被销毁,因为它是一个本地函数。此时,临时将被销毁。所有这些的净效果是s立即成为悬挂参考。

请注意,对于原始for_each,函数将按值返回,并且引用将直接绑定到它,从而延长其生命周期。

Herb Sutter在这次cppcon谈话中谈到了这一点:https://www.youtube.com/watch?v=hEx5DNLWGgA。基本上,当你从一个函数返回一个引用/指针时,关于它实际指向的内容的安全选项很少。通过引用获取的函数参数是其中之一,但const ref和rvalue ref的函数参数不是因为它们吸引临时并导致函数返回悬挂。我在这里写过这个主题:http://www.nirfriedman.com/2016/01/18/writing-good-cpp-by-default-in-the-stl/

如果你想采用仿函数并避免构造对象,我只需要通过转发参考并且不返回任何内容。为什么?因为,如果用户之后不需要仿函数,他们可以传递一个右值而不用担心误用返回。如果他们确实需要它,他们可以构建它,将它作为左值传递,然后在之后使用它。

template< class InputIt, class UnaryFunction >
void my_for_each( InputIt first, InputIt last, UnaryFunction&& f);

my_functor s{};
my_for_each(v.begin(), v.end(), s);
auto sum = s.sum;

没有施工/破坏,没有问题。虽然我会警告说如果你出于性能原因试图避免这种情况,除非你的functor / lambda很大,否则它可能是误导的,这是不寻常的。