move-semantics 相关问题

移动语义是一种编程语言功能,当源对象是临时对象或其他过期对象时,允许复制操作被更有效的“移动”替换。

为什么添加默认的移动赋值运算符会破坏标准交换函数的编译?

在下面的代码中,如果移动赋值未注释,则交换函数将停止程序的编译。我在所有 3 个主要编译器(GCC、Clang、MSVC)上观察到了这种行为。 #包括 在以下代码中,如果未注释移动分配,则交换函数将停止程序的编译。我在所有 3 个主要编译器(GCC、Clang、MSVC)上都观察到了这种行为。 #include <utility> #include <memory> struct test { test() = default; test(test&& other) noexcept = default; //test& operator=(test&& other) noexcept = default; test(const test& other) : ptr(std::make_unique<int>(*other.ptr)) {} test& operator=(test other) noexcept { std::swap(*this, other); return *this; } std::unique_ptr<int> ptr; }; Godbolt 测试:https://godbolt.org/z/v1hGzzEaz 研究标准库实现,他们使用 SFINAE 或概念来启用/禁用 std::swap 重载,并且当特殊函数未注释时,由于某种原因,某些特征会失败(libstdc++ 上的 is_move_constructible 和/或 is_move_assignable)。 我的问题是:为什么添加默认的特殊成员函数会阻止标准库将类型视为可移动? std::move的主要实现在内部使用移动分配,类似于。 template <typename T> void swap(T& a, T& b) { T c = std::move(a); a = std::move(b); b = std::move(c); } 这意味着移动分配需要对您的类型有效,但事实并非如此。 如果你调用移动赋值运算符,你会得到一个错误: <source>:18:15: error: use of overloaded operator '=' is ambiguous [...] [...] | <source>:9:11: note: candidate function 9 | test& operator=(test&& other) noexcept = default; | ^ <source>:15:11: note: candidate function 15 | test& operator=(test other); | ^ 这两个运算符都可以使用 xvalue 进行调用,并且两者都不是更好的匹配。 std::swap 受到限制,因此只有 MoveAssignable 类型可以交换,而你的类型则不能。 即使你可以打电话std::swap,你也不能同时打电话 依赖 std::swap 的默认实现,它使用 operator= 用 operator= 定义 std::swap 这将是无限递归,因为 = 和 std::swap 的定义是循环的。 解决方案 您可以为您的类型定义自定义 swap,这样您就不再依赖 std::swap。 只保留 operator=(test),看起来像: test& operator=(test other) noexcept { swap(*this, other); // custom function, not std::swap return *this; } 您还可以手动定义单独的 operator=(const test&) 和 operator=(test&&) ,以便 std::swap 将使用移动赋值运算符,并且重载决策中不会有歧义。

回答 1 投票 0

std::move 和 std::shared_ptr 参数按值传递

考虑以下代码: #包括 #包括 班级测试 { 民众: 测试(int t):t(t){} 整数t; }; 无效测试(std::shared_ptr t) { 标准...

回答 1 投票 0

为什么C++中的移动语义在函数签名中有右值引用

考虑下面的移动赋值运算符: 类我的类{ 私人的: ssize_t buf_size; 无效* buf_ptr; 民众: MyClass &operator=(MyClass &&rhs) { 如果(这个!= &rhs){

回答 1 投票 0

编译器可以删除特定数据成员的副本吗?

获取此代码: struct Bar { //有非默认的复制构造函数 酒吧()=默认; 酒吧(常量酒吧&){} }; struct LargeObject { //有默认的复制构造函数 std::数组 获取此代码: struct Bar { //has non-default copy-constructor Bar() = default; Bar(const Bar&) {} }; struct LargeObject { //has default copy-constructor std::array<char, 1000> m_chars; }; struct Foo { Bar m_bar; LargeObject m_obj; }; Foo func() { Foo f; return f; } 当用户指定复制构造函数时,编译器无法删除副本。当类的成员具有非默认复制构造函数时该怎么办?在此代码中,当从 f 返回 func 时,编译器是否可以删除 m_obj 的副本,还是必须复制所有内容? 你的问题是基于一个错误的前提。编译器完全有权删除这样的副本,无论类本身或其成员之一是否具有非默认复制(或移动)构造函数。自 C++11 以来引入的复制省略规则的好处是允许这种优化,即使它违反了 as-if 规则。 如果做不到这一点,复制省略就没有那么有用了;即使只有一个智能指针(具有自定义复制和移动构造函数)的类也没有资格使用 RVO,除非编译器可以肯定地证明没有违反 as-if 规则。 在您的特定场景中,编译器可以在启用优化的情况下(这不是强制性的,因为您依赖于非强制性 NRVO,而不是简单 RVO 的 C++17 保证复制省略)通过以下方式实现 func直接将 Foo 对象构建到调用者提供的存储中,而不使用必须移动/复制回调用者的单独 Foo f。

回答 1 投票 0

运算符重载:修改临时对象或创建新对象

我在我们的项目中看到了以下代码,并问自己有什么技术和心理影响: A类{ 民众: A(常量 A&); A(A &&); 〜A(); 操作...

回答 2 投票 0

我自己的智能指针实现正在创建移动操作问题

在下面的代码中,输出结果为空白,而不是构造函数和析构函数的打印输出。没有移动操作,代码运行良好。我不知何故搞乱了移动操作,

回答 1 投票 0

使用用户定义的构造函数移动类型的构造,该构造函数接受使用不完整类型实例化的模板类型参数

试图理解为什么这段代码不能用 gcc 和 clang 编译,但可以用 msvc 编译。 编译器资源管理器 #包括 类不完整; 模板 结构

回答 1 投票 0

当使用C++的移动语义时,内存中会发生什么?

我试图理解C++的移动语义、移动构造函数、移动赋值运算符、std::move()。 让我们考虑以下示例: #包括 无效 swapWithMove(int& ...

回答 1 投票 0

为什么std::move中使用std::remove_reference?

我尝试实现 std::move,它使用 std::remove_reference,但似乎没有它也能工作。请给我一个例子,如果没有 std::remove_reference,我的实现将会失败。

回答 2 投票 0

匹配多个盒装值

我有以下代码,可以正常编译 #[导出(调试、PartialEq、克隆)] pub 枚举表达式 { 常量(i32), 负(方框), 添加(框,框 我有以下代码,可以正常编译 #[derive(Debug, PartialEq, Clone)] pub enum Expression { Const(i32), Neg(Box<Expression>), Add(Box<Expression>, Box<Expression>), } fn simplify(expr: &Expression) -> Expression { match expr { Expression::Neg(x) => match **x { Expression::Const(n) => Expression::Const(-n), _ => expr.clone() }, // GIVES ERROR // Expression::Add(x, y) => match (**x, **y) { // (Expression::Const(n), Expression::Const(m)) => Expression::Const(n + m), // _ => expr.clone() // }, Expression::Add(x, y) => match **x { Expression::Const(n) => match **y { Expression::Const(m) => Expression::Const(n + m), _ => expr.clone() } _ => expr.clone() } _ => expr.clone() } } 但是,如果我用注释掉的版本替换 Expression::Add 臂,则会出现以下编译器错误 error[E0507]: cannot move out of `**x` which is behind a shared reference --> src/lib.rs:21:41 | 21 | Expression::Add(x, y) => match (**x, **y) { | ^^^ move occurs because `**x` has type `Expression`, which does not implement the `Copy` trait error[E0507]: cannot move out of `**y` which is behind a shared reference --> src/lib.rs:21:46 | 21 | Expression::Add(x, y) => match (**x, **y) { | ^^^ move occurs because `**y` has type `Expression`, which does not implement the `Copy` trait For more information about this error, try `rustc --explain E0507`. 是否有理由我们可以与单独的 **x 匹配,但不能在像 (**x, **y) 这样的元组中匹配?前者实际上是被转换还是隐藏了一些语法糖?有没有比两个嵌套匹配更简单的方法来编写这个 Add 臂? 编辑:我还看到有一个 ref 关键字,它应该解决类似的问题,但是将我的元组匹配表达式更改为 (ref **x, ref **y) 会产生语法错误(error: expected expression, found keyword ref)。 TL;DR: 与 (&**x, &**y) 比赛。 这里发生的事情很有趣。 TL;DR 是:当您 match v {} 时,您不会阅读 v。您为 v 创建了一个 地方。 A place 是我们可以阅读的东西。或者写信给。或者什么也不做。重要的是,仅仅创造场所并不涉及这样的操作。您可以稍后读/写它,但是当您创建它时,它只是一个地方。 在您的 match 中,x 和 y 的类型为 &Box<Expression>。当我们match **x时,我们不读x。因此,我们也不会移动**x。我们所做的是为**x创造一个地方。然后我们将这个地方与 Expression::Const(n) 进行匹配。现在我们读取 x 并从中提取 n。但是 n 是 i32 - Copy - 所以这很好。 相反,当您使用元组 (**x, **y) 时,由于您不直接与 **x 和 **y 匹配,因此您确实会读取它们。因为你读了它们,而它们不是 Copy (Expression),所以你就离开了它们。现在这是一个错误,因为您无法移出共享引用。您之后匹配它们,但它们已经移动了。 你可以试试match(x.as_ref(),y.as_ref())

回答 2 投票 0

在构造函数中将对象移动到成员

我有几个类,其中的对象是另一个类的成员。 ARX类; PID类; 类模拟 { 阿克斯阿克斯; PID; }; 我想在“外部”构建所有成员,......

回答 0 投票 0

将 std::move shared_ptr 与条件运算符一起使用时的奇怪行为

我在 shared_ptr 上使用 std::move 编写一些 C++ 代码,得到了非常奇怪的输出。我已经简化了我的代码如下 int func(std::shared_ptr&& a) { 返回 0; } 国际马...

回答 0 投票 0

高阶函数参数中的`&&`可以作为转发引用吗?

转发(或通用引用)的规范示例如下: 模板 void f(T&& 参数); IE。 “如果声明变量或参数具有类型......

回答 1 投票 0

是否保证容器会在 std::move 期间销毁现有对象?

考虑以下示例: #包括 #包括 A类{ 民众: A(const A&) = 默认值; A(A&&) = 默认; A& 运算符=(const A&) =

回答 1 投票 0

在 x 值容器上进行适当的范围循环

由于 RVO 是标准保证,因此通常具有容器返回功能,例如: std::function get_elements() { ... } 在这种情况下,我见过两种风格的 range-for ite...

回答 0 投票 0

移动唯一指针 - cppreference 上的未定义行为?

我有一个后续问题:Move unique_ptr: reset the source vs. destroy the old object 为了快速总结原始问题,cppreference 上有这个示例代码: 结构...

回答 2 投票 0

如何在函数返回期间使用 std::string 的移动语义? [重复]

可能重复: C++11 右值和移动语义混淆 我认为正确的是 std::string GetLine() { 标准::字符串海峡; std::getline(std::cin, 海峡); 返回 std::move(str); }...

回答 3 投票 0

移动语义对 C++ 中数据结构的影响

我正在创建一个 stl 双端队列作为 unique_ptr 的类成员,并在其中一个类成员函数中返回带有移动语义的双端队列。我不确定班级成员d的状态...

回答 0 投票 0

无法从 initializer_list 移动的解决方法?

方便的 initializer_list 语法似乎以无法移动列表成员为代价,创建了不必要的副本。 结构A { // 一些成员是动态资源...

回答 1 投票 0

移动局部变量没问题,但是移动它的局部变量地址一定是编译时错误?为什么我没有收到错误信息?

我正在观看 herb sutter cpp2 的视频,其中说移动局部变量的地址是 C++ 中的编译时错误,但是当我尝试移动局部变量的地址时,我没有得到...

回答 0 投票 0

© www.soinside.com 2019 - 2024. All rights reserved.