为什么这里调用move构造函数

Why is the move constructor called here?

本文关键字:构造函数 move 调用 这里 为什么      更新时间:2023-10-16

下面是一个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");
}

程序的输出是什么?

我是这样想的:

    调用
  1. f(X a),构造函数隐式地将const char*转换为X,因此输出为1
  2. 由于没有存储返回值的对象,返回值被丢弃,无输出
  3. 呼叫
  4. g(const char*), X c(b) X(const char*) 输出为1
  5. 返回值再次被丢弃- 无输出

所以答案是11。测验给出的答案是131。我用g++ 4.4.4-13得到的答案是121。

据说这段代码是用这个命令编译的:

g++ -std=c++11 -Wall -Wextra -O -pthread

中间的数是从哪里来的?为什么可以是3或2呢?

理论上,这可以打印1311331313131331中的任何一个。这是一个相当愚蠢的测验问题。

  • f("hello");:

    • "hello"通过转换构造函数转换为临时X,打印1
    • 临时X用于初始化函数参数,调用move构造函数,打印3
    • x用于初始化临时返回值,调用move构造函数,打印3。这是一个函数参数,所以不允许省略,但是返回值是隐式的移动。
  • g("hello");

    • "hello"用于通过转换构造函数构造c,打印1
    • c用于初始化临时返回值,调用move构造函数,打印3

记住,函数总是必须构造它们返回的东西,即使它被调用代码丢弃了。

至于打印2,那是因为您正在使用的旧编译器没有实现隐式的-move-when-returning-a-local-variable规则。

复制省略适用于g中的return语句,也可能适用于其他地方。引用自cppreference:

复制省略是唯一可以改变的优化形式可观察到的副作用。因为有些编译器不执行在允许省略的任何情况下复制省略(例如,在调试中)模式),依赖于复制/移动的副作用的程序构造函数和析构函数是不可移植的

因此,对于您的示例代码,无法可靠地预测跨不同实现的输出。

相关文章: