右值引用,std::reference_wrappers 和 std::function
rvalue references, std::reference_wrappers and std::function
我正在阅读 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;
}
- std::function 的 r 值引用实际上如何接受 l 值,例如。如果
Greeting g("World",eng)
,类似的左值对于任何其他参数都是不可接受的(除了模板化构造函数和制作通用引用之外?)?
当 std::ref 传递给 std::function 时实际发生了什么,ref 提到仅转发参数。但是,如果我在显示注释掉的 g4 时移动函数对象本身,我会看到 g2 的输出,它使用 std::ref 实际看到移动的效果,只是打印 world2
移动后可调用对象发生了什么,字符串本身被移动了,但是函数仍然有效? (对于类型不同的函数对象,比如 struct f{void operator()() { //something })
,这是否意味着 f 在移动后可能有效?)
那是因为 none 您实际创建的对象是 std::function,它们是可调用对象,可用于创建临时 std::function。据我所知,最后一点(例如,我没有声称这是真的,我假设由于我的懒惰,见下文)是 UB,因为从对象移动可以保留在任何有效状态,所以不保证字符串成员实际上是空的。
根据经验,以不需要任何先决条件的方式使用从对象移动(重新分配,检查字符串/ vec 是否为空,等等)。
为了澄清,让我们看一下 std::function 构造函数 here,有问题的构造函数是 (5):
template< class F >
function( F f );
因此,当您尝试使用可调用对象构建 std::function 时,默认情况下您将创建可调用对象的副本。你规避它,例如通过使用 std::ref,这将导致对可调用对象的后续更改反映在使用它的 std::function 中(因为那时您实际上创建了 "from ref",而不是像往常一样通过复制)。
我正在阅读 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;
}
- std::function 的 r 值引用实际上如何接受 l 值,例如。如果
Greeting g("World",eng)
,类似的左值对于任何其他参数都是不可接受的(除了模板化构造函数和制作通用引用之外?)? 当 std::ref 传递给 std::function 时实际发生了什么,ref 提到仅转发参数。但是,如果我在显示注释掉的 g4 时移动函数对象本身,我会看到 g2 的输出,它使用 std::ref 实际看到移动的效果,只是打印 world2
移动后可调用对象发生了什么,字符串本身被移动了,但是函数仍然有效? (对于类型不同的函数对象,比如 struct
f{void operator()() { //something })
,这是否意味着 f 在移动后可能有效?)
那是因为 none 您实际创建的对象是 std::function,它们是可调用对象,可用于创建临时 std::function。据我所知,最后一点(例如,我没有声称这是真的,我假设由于我的懒惰,见下文)是 UB,因为从对象移动可以保留在任何有效状态,所以不保证字符串成员实际上是空的。
根据经验,以不需要任何先决条件的方式使用从对象移动(重新分配,检查字符串/ vec 是否为空,等等)。
为了澄清,让我们看一下 std::function 构造函数 here,有问题的构造函数是 (5):
template< class F >
function( F f );
因此,当您尝试使用可调用对象构建 std::function 时,默认情况下您将创建可调用对象的副本。你规避它,例如通过使用 std::ref,这将导致对可调用对象的后续更改反映在使用它的 std::function 中(因为那时您实际上创建了 "from ref",而不是像往常一样通过复制)。