在实践中什么时候调用移动构造函数?
When is a move constructor called in practice?
我最近学习了移动构造函数,但是很多在线资源都没有谈论复制省略。复制省略对我来说也很有意义,但它让我想知道如果没有超级人为的例子,什么时候会调用移动构造函数。
来自一个流行的 SO post,它向我解释了移动语义
string b(x + y);
string c(some_function_returning_a_string());
post 说这两个都应该调用移动构造函数,因为它们接收临时对象。但是,其中 none 实际上调用了移动构造函数(我有 tested),相反它们都只是进行复制省略,除非您通过显式编写 std::move
.[=19 来强制它这样做=]
string b(std::move(x + y));
string c(std::move(some_function_returning_a_string()));
或some_function_returning_a_string
returnsstd::move(someString)
。但你为什么要这样做?复制省略甚至比移动语义更高效。那么在哪些自然情况下会调用移动构造函数而不是复制省略?
在你点我之前,我觉得 When Does Move Constructor get called? 回答给出了人为的例子,或者其中一些只是复制省略。我有兴趣了解在实践中何时调用移动构造函数。
这里有一个 link 用于测试 https://godbolt.org/z/KfczbboTr
对发布的问题的非常技术性的回答。这里会调用move构造函数,我觉得,代码一点都不做作。
struct Foo
{
Foo(std::string str) : str(std::move(str)) { }
std::string str;
}
在使用 std::tuple<>
、std::pair<>
或 std::variant<>
等包装器类型时,移动构造总是在临时对象和 RValues 上隐式调用。添加的间接级别可防止复制省略,您最终会调用移动构造函数。
例如:
#include <tuple>
#include <iostream>
struct SomeType {
SomeType() = default;
SomeType(SomeType&&) {
std::cout << "moved!\n";
}
};
int main() {
std::make_tuple(SomeType{}, 12, SomeType{});
}
输出如下:
moved!
moved!
在您的示例中,移动对象是临时对象,但移动时并非总是如此。有时我们知道我们可以移动,因为移动的对象将不再使用,即使它不是临时的。考虑这种类型:
struct foo {
foo() = default;
foo(foo&& f) noexcept {
std::cout << "move\n";
}
};
当您创建一个 foo
向量并且向量重新分配时,它不会复制元素,但会移动它们。例如:
#include <iostream>
#include <vector>
int main() {
std::vector<foo> v;
v.resize(5);
v.resize(v.capacity()+1); // force the vector to reallocate
}
move
move
move
move
move
无法删除复制或移动,因为元素位于旧位置并且必须以某种方式到达内存中的新位置。
我最近学习了移动构造函数,但是很多在线资源都没有谈论复制省略。复制省略对我来说也很有意义,但它让我想知道如果没有超级人为的例子,什么时候会调用移动构造函数。
来自一个流行的 SO post,它向我解释了移动语义
string b(x + y);
string c(some_function_returning_a_string());
post 说这两个都应该调用移动构造函数,因为它们接收临时对象。但是,其中 none 实际上调用了移动构造函数(我有 tested),相反它们都只是进行复制省略,除非您通过显式编写 std::move
.[=19 来强制它这样做=]
string b(std::move(x + y));
string c(std::move(some_function_returning_a_string()));
或some_function_returning_a_string
returnsstd::move(someString)
。但你为什么要这样做?复制省略甚至比移动语义更高效。那么在哪些自然情况下会调用移动构造函数而不是复制省略?
在你点我之前,我觉得 When Does Move Constructor get called? 回答给出了人为的例子,或者其中一些只是复制省略。我有兴趣了解在实践中何时调用移动构造函数。
这里有一个 link 用于测试 https://godbolt.org/z/KfczbboTr
对发布的问题的非常技术性的回答。这里会调用move构造函数,我觉得,代码一点都不做作。
struct Foo
{
Foo(std::string str) : str(std::move(str)) { }
std::string str;
}
在使用 std::tuple<>
、std::pair<>
或 std::variant<>
等包装器类型时,移动构造总是在临时对象和 RValues 上隐式调用。添加的间接级别可防止复制省略,您最终会调用移动构造函数。
例如:
#include <tuple>
#include <iostream>
struct SomeType {
SomeType() = default;
SomeType(SomeType&&) {
std::cout << "moved!\n";
}
};
int main() {
std::make_tuple(SomeType{}, 12, SomeType{});
}
输出如下:
moved!
moved!
在您的示例中,移动对象是临时对象,但移动时并非总是如此。有时我们知道我们可以移动,因为移动的对象将不再使用,即使它不是临时的。考虑这种类型:
struct foo {
foo() = default;
foo(foo&& f) noexcept {
std::cout << "move\n";
}
};
当您创建一个 foo
向量并且向量重新分配时,它不会复制元素,但会移动它们。例如:
#include <iostream>
#include <vector>
int main() {
std::vector<foo> v;
v.resize(5);
v.resize(v.capacity()+1); // force the vector to reallocate
}
move
move
move
move
move
无法删除复制或移动,因为元素位于旧位置并且必须以某种方式到达内存中的新位置。