rvalue ссылки, std :: reference_wrappers и std :: function - PullRequest
0 голосов
/ 29 августа 2018

Я читал ссылки на 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 голосов
/ 29 августа 2018

Это связано с тем, что ни один из создаваемых вами объектов на самом деле не является функцией std ::, они могут быть вызваны для создания временных функций std :: functions. И последнее, насколько мне известно (например, я не утверждаю, что это правда, я предполагаю, что из-за моей лени, см. Ниже), это UB, так как перемещенный из объекта может быть оставлен в любом допустимом состоянии, поэтому нет гарантирует, что строковый член действительно будет пустым.
Как правило, используйте перемещенные объекты таким образом, чтобы не требовалось никаких предварительных условий (переназначить, проверить, не заполнены ли строки / vec и т. Д.).
Чтобы уточнить, давайте посмотрим на конструкторы std :: function здесь , рассматриваемый конструктор (5):

template< class F >
function( F f ); 

Поэтому, когда вы пытаетесь создать функцию std :: с вызываемой функцией, вы по умолчанию создаете копию вызываемой. Вы обходите это, например используя std :: ref, что приведет к тому, что последующие изменения вызываемого объекта будут отражены в std :: function с его использованием (как тогда вы на самом деле создаете «из ref», а не из копирования как обычно).

...