自动检测C++14 "return should use std::move"情况
Automatically detect C++14 "return should use std::move" situation
我的理解是,在C 17中,以下片段旨在做正确的事情:
struct Instrument; // instrumented (non-trivial) move and copy operations
struct Base {
Instrument i;
};
struct Derived : public Base {};
struct Unrelated {
Instrument i;
Unrelated(const Derived& d): i(d.i) {}
Unrelated(Derived&& d): i(std::move(d.i)) {}
};
Unrelated test1() {
Derived d1;
return d1;
}
Base test2() {
Derived d2;
return d2; // yes, this is slicing!
}
也就是说,在C 17中,编译器应同时将d1
和d2
视为RVALUE,以便在这两个返回语句中过载分辨率。但是,在C 14和更早的时候,情况并非如此。return
操作数中的LVALUE到RVALUE转换仅在操作数正好正确的返回类型时适用。
此外,GCC和Clang在这一领域似乎都有令人困惑的和可能的越野车行为。在Wandbox上尝试上述代码,我看到了这些输出:
GCC 4.9.3 and earlier: copy/copy (regardless of -std=)
Clang 3.8.1 and earlier: copy/copy (regardless of -std=)
Clang 3.9.1 and later: move/copy (regardless of -std=)
GCC 5.1.0 through 7.1.0: move/copy (regardless of -std=)
GCC 8.0.1 (HEAD): move/move (regardless of -std=)
因此,这开始是一个工具问题,最终以" C 编译器的正确行为是什么?"
>我的工具问题是:在我们的代码库中,我们有几个位于return x;
的地方,但这意外产生了副本而不是移动,因为我们的工具链是GCC 4.9.x和/或clang。我们想自动检测到这种情况,并根据需要插入std::move()
。有什么简单的方法可以检测此问题吗?也许我们可以启用clang-tidy检查或-Wfoo
标志?
但是,当然,现在我也想知道C 编译器在此代码上的正确行为是什么。这些输出是否指示GCC/CLANG错误?他们正在工作吗?语言版本(-std=
(应该很重要吗?(我认为这应该很重要,除非通过缺陷报告更新了正确的行为,直到C 11。(
这是一个受巴里答案启发的更完整的测试。我们测试了六种不同的情况,其中应需要LVALUE转换转换。
GCC 4.9.3 and earlier: elided/copy/copy/copy/copy/copy
Clang 3.8.1 and earlier: elided/copy/copy/copy/copy/copy
Clang 3.9.1 and later: elided/copy/move/copy/copy/copy
GCC 5.1.0 through 7.1.0: elided/copy/move/move/move/move
GCC 8.0.1 (HEAD): elided/move/move/move/move/move
ICC 17: elided/copy/copy/copy/copy/copy
ICC 18: elided/move/move/move/copy/copy
MSVC 2017 (wow): elided/copy/move/copy/copymove/copymove
在巴里的回答之后,在我看来,在所有情况下,Clang 3.9 在技术上正确的事情做到了。在所有情况下,GCC 8 在所有情况下都做可取的事情;总的来说,我应该停止教导人们"只有return x
并让编译器DTRT"(或至少要用巨大的闪烁警告教(,因为在实践中,编译器将 not> not> dtrt,除非您使用出血边缘(技术不合格(gcc。
正确的行为是移动/复制。您可能只想写一个clang-tidy的支票。
C 17中的措辞是[class.copy.elision]/3,C 14中的措辞为[class.copy]/32。特定的单词和格式不同,但规则是相同的。
在C 11中,规则措辞为[class.copy]/32,并与复制责任规则绑定,自动存储本地变量的例外是在CWG 1579中作为缺陷报告。该缺陷报告之前的编译器将以复制/复制的形式行为。但是,由于缺陷报告针对C 11,实施措辞更改的编译器将在所有标准版本中实施它。
使用C 17措辞:
在以下复制委托上下文中,可以使用移动操作代替复制操作:
- 如果返回语句中的表达式是(可能是括号内的(ID表达式,该命名为一个自动存储持续时间的对象,或者在主体中声明的自动存储持续时间或最内置的封闭函数或lambda-expression的参数 - 删除范围,或
- [...]
超载分辨率首先要选择副本的构造函数,就像由rvalue指定的对象一样。如果第一个过载分辨率失败或未执行,则或所选构造函数的第一个参数的类型不是对对象类型的RVALUE引用(可能是cv qualified(,则执行过载分辨率。同样,将物体视为lvalue。
in:
Unrelated test1() {
Derived d1;
return d1;
}
我们遇到了第一个子弹,因此我们尝试使用Derived
类型的rvalue复制Unrelated
,这给我们提供了Unrelated(Derived&& )
。符合突出显示的标准,因此我们使用它,结果是移动。
in:
Base test2() {
Derived d2;
return d2; // yes, this is slicing!
}
我们再次遇到第一个子弹,但是超载分辨率将找到Base(Base&& )
。所选构造函数的第一个参数是不是对Derived
(可能是CV -qualified(的RVALUE引用,因此我们再次执行超载分辨率 - 最终复制。
- 在没有太多条件句的情况下,我如何避免被零除
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 函数中堆分配的效果与缺少堆分配的情况
- 在未初始化映射的情况下,将值插入到映射的映射中
- 瓦尔格林德:数学函数"Conditional jump or move depends on uninitialised value(s)"
- Usages of std::move
- 是默认情况下分配给char数组常量的值
- 使用仅使用一次的变量调用的复制构造函数.这可能是通过调用move构造函数进行编译器优化的情况吗
- 如何在没有 std::move 的情况下移动临时对象
- 自动检测C++14 "return should use std::move"情况
- 在无法完成 RVO 的情况下,我们应该写"std::move"吗?
- 在哪些情况下,从 std::forward 分配比从 std::move 分配更可取?为什么
- 是STD :: Move多余的情况
- 如果在没有move构造函数的情况下移动对象,会发生什么
- std::move( ) 在没有 move-ctor 的情况下调用 copy-ctor.为什么以及如何预防它
- 在多个return语句的情况下,返回时带有“std::move”明智
- 为什么在没有std::move的情况下不调用move构造函数
- 如何在没有 std::move 的情况下按值返回unique_ptr
- 是否存在与std::move相反的情况
- 应用 std::move 后对象实例会发生什么情况