通过通用引用返回

Return by universal reference

本文关键字:返回 引用      更新时间:2023-10-16

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内部,用std::forward<UnaryFunction>(f)(...)调用f,我可以避免移动构造参数的成本,并且作为奖励,能够尊重引用限定符。但我真的不确定我应该返回什么。如果我这样做:

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

坏事(例如,悬而未决的引用)会发生吗?

(当我在这篇文章中设计for_each时,这个问题会出现。

正如另一个精细答案所指出的,传递const&和右值引用参数是危险的,因为引用寿命延长不会交换。

当您想要传递转发引用T&&时,返回的正确内容是 T 。 这会将右值转换为临时值,并将左值保留为引用。

所以:

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

对于临时f,它会创建一个(移动到)副本。

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

坏事可以而且将会发生。

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的进入,现在延长了其生存期。当函数退出时,它将返回对临时函数的右值引用。但是,当函数退出时,临时最初绑定到的右值引用将被销毁,因为它是本地函数。到那时,临时将被销毁。所有这一切的净效果是,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;
没有

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