右值引用,std :: reference_wrappers和std :: function

时间:2018-08-29 09:26:34

标签: c++11 c++17 rvalue-reference std-function reference-wrapper

我正在阅读r值引用并移动语义。不幸的是,使用std :: function和std :: reference_wrapper进行此实验使我感到困惑。

#include <iostream>
#include <string>
#include <string_view>
#include <functional>

class Greeting {
  std::string g;
  std::function <void(std::string_view)> f;
public:
  Greeting(std::string&& _g, std::function<void(std::string_view)>&& _f)
    : g(std::move(_g)), f(std::move(_f)){};
  void greet() {
    f(g);
  }
};

struct prefix_g {
  std::string g;
public:
  prefix_g(const std::string&& _g) : g(std::move(_g)) {}
  void operator() (std::string_view s) {
    std::cout <<g <<" "<< s << std::endl;
  }
};

int main() {
  prefix_g eng("Hello");

  Greeting g("World",eng);
  Greeting g2("World2",std::ref(eng)); // reference wrapper, special
                                       // forwarding for op ()
  std::string s3("world3"), s4("world3");

  // Greeting g3(std::ref(s3), std::ref(eng)); won't compile; &s3 -> &&s3
  // Greeting g3(s3, eng); won't compile lval to rval
  // Greeting g4(std::move(s4), std::move(eng)); // compiles, output Hello World2 -> World2 as g is moved?

  g.greet(); g2.greet();
  Greeting g4(std::move(s4), std::move(eng));
  g4.greet();

  Greeting g5("world5", std::move(eng)); // UB? move guarantees fn object is
                                         // still valid, ofc, g now gets default
                                         // init to empty
  g5.greet();
  return 0;
}
  1. r值对std :: function的引用实际上如何接受例如的l值。在Greeting g("World",eng)的情况下,其他任何参数都不能接受类似的l值(除了对构造函数进行模板化和进行通用引用之外?)?
  2. 将std :: ref传递给std :: function时实际发生的情况,ref提到仅转发参数。但是,如果按注释出的g4所示移动函数对象本身,我会看到g2的输出,该输出使用std :: ref实际看到了生效,只是打印world2

  3. 可移动对象在移动后会发生什么变化,字符串本身已被移动,但是该功能仍然有效? (对于另一个类型为struct f{void operator()() { //something })的函数对象,这是否意味着f在移动之后可能有效?)

1 个答案:

答案 0 :(得分:1)

这是因为您创建的对象实际上都不是std :: function,它们是可用于创建临时std :: functions的可调用对象。据我所知,最后一点(例如,我并不是说这是真的,我假设是由于我的懒惰,请参见下文)是UB,因为从对象移出的对象可以处于任何有效状态,因此保证字符串成员实际上为空。
根据经验,使用无对象的移动对象不需要任何先决条件(重新分配,检查字符串/ vec是否为空等是否合格)。
为了明确起见,让我们看一下std :: function构造器here,有问题的构造器为(5):

template< class F >
function( F f ); 

因此,当您尝试构造带有可调用对象的std :: function时,默认情况下将创建该可调用对象的副本。您可以规避它,例如通过使用std :: ref,它将导致对可调用对象的后续更改反映在使用它的std :: function中(因为那样的话,您实际上创建的是“来自ref”,而不是像往常一样是通过复制创建的。)