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
时会出现此问题。)
答案 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很大,否则它可能是误导的,这是不寻常的。