我正在阅读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;
}
Greeting g("World",eng)
的情况下,其他任何参数都不能接受类似的l值(除了对构造函数进行模板化和进行通用引用之外?)? 将std :: ref传递给std :: function时实际发生的情况,ref提到仅转发参数。但是,如果按注释出的g4所示移动函数对象本身,我会看到g2的输出,该输出使用std :: ref实际看到了生效,只是打印world2
可移动对象在移动后会发生什么变化,字符串本身已被移动,但是该功能仍然有效? (对于另一个类型为struct f{void operator()() { //something })
的函数对象,这是否意味着f在移动之后可能有效?)
答案 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”,而不是像往常一样是通过复制创建的。)