为什么这里调用move构造函数
Why is the move constructor called here?
下面是一个c++测试的代码示例:
#include <iostream>
struct X {
X(const char *) { std::cout << 1; }
X(const X &) { std::cout << 2; }
X(X &&) { std::cout << 3; }
};
X f(X a) {
return a;
}
X g(const char * b) {
X c(b);
return c;
}
int main() {
f("hello");
g("hello");
}
程序的输出是什么?
我是这样想的:
- 调用
-
f(X a)
,构造函数隐式地将const char*
转换为X,因此输出为1 - 由于没有存储返回值的对象,返回值被丢弃,无输出 呼叫
-
g(const char*)
,X c(b)
X(const char*)
输出为1 - 返回值再次被丢弃- 无输出
所以答案是11。测验给出的答案是131。我用g++ 4.4.4-13得到的答案是121。
据说这段代码是用这个命令编译的:
g++ -std=c++11 -Wall -Wextra -O -pthread
中间的数是从哪里来的?为什么可以是3或2呢?
理论上,这可以打印131
、13313
、1313
和1331
中的任何一个。这是一个相当愚蠢的测验问题。
-
f("hello");
:- "hello"通过转换构造函数转换为临时
X
,打印1
。 - 临时
X
用于初始化函数参数,调用move构造函数,打印3
。 -
x
用于初始化临时返回值,调用move构造函数,打印3
。这是一个函数参数,所以不允许省略,但是返回值是隐式的移动。
- "hello"通过转换构造函数转换为临时
-
g("hello");
- "hello"用于通过转换构造函数构造
c
,打印1
。 -
c
用于初始化临时返回值,调用move构造函数,打印3
。
- "hello"用于通过转换构造函数构造
记住,函数总是必须构造它们返回的东西,即使它被调用代码丢弃了。
至于打印2
,那是因为您正在使用的旧编译器没有实现隐式的-move-when-returning-a-local-variable规则。
复制省略适用于g
中的return
语句,也可能适用于其他地方。引用自cppreference:
复制省略是唯一可以改变的优化形式可观察到的副作用。因为有些编译器不执行在允许省略的任何情况下复制省略(例如,在调试中)模式),依赖于复制/移动的副作用的程序构造函数和析构函数是不可移植的
因此,对于您的示例代码,无法可靠地预测跨不同实现的输出。
相关文章:
- 使用仅使用一次的变量调用的复制构造函数.这可能是通过调用move构造函数进行编译器优化的情况吗
- 添加自定义析构函数时,Move 构造函数在派生类中消失
- 将向量从 N1 缩小到 N2 项,而不触发默认构造函数并仅使用 move 语义
- 为什么 std::move 不将默认移动构造函数中的源变量更改为默认值?
- std::映射,只有move构造函数可用
- 为什么我的代码中没有调用move构造函数
- std::转换move构造函数的模板专业化的变体
- std::tuple默认构造函数,带有move可构造元素
- 如何调用move构造函数
- 复制初始化:为什么即使关闭了复制省略,也没有调用move或copy构造函数
- 为什么我们需要在构造函数的初始化列表中使用 std::move?
- 在构造函数中初始化成员时,是否应该在成员上使用 std::move?
- 我可以将 std::move 与不提供 move 构造函数的类一起使用吗?
- 基于范围的 std::move 调用意外复制构造函数
- C++编译错误是由于使用 std::move 时运动构造函数与其他非运动构造函数之间的冲突
- 为什么noexcept move构造函数在向量重新分配期间没有被调用
- 使用 Move 语义在构造函数中初始化类成员
- 为什么在声明析构函数时必须声明 copy & move 构造函数?
- 我应该总是在构造函数中使用 std::move 吗?
- 如果我们只定义复制构造函数/oper=,为什么移动构造函数/move赋值没有隐式声明和定义为删除