C++ 这里的具体操作顺序是什么?

C++ 这里的具体操作顺序是什么?,c++,c++11,C++,C++11,我有一个功能: std::string makeMeat() { return "Pork"; } 在代码的某个地方,我这样使用它: std::string meat = makeMeat(); 我想知道在这行代码上进行的操作的确切顺序是什么。假设两种不同的情况: string没有移动构造函数(例如) string具有move构造函数 我猜makeMeat()创建类std::string的临时对象 std::string temp("Pork"); 在这之后,std::string mea

我有一个功能:

std::string makeMeat() { return "Pork"; }
在代码的某个地方,我这样使用它:

std::string meat = makeMeat();
我想知道在这行代码上进行的操作的确切顺序是什么。假设两种不同的情况:

  • string没有移动构造函数(例如)
  • string具有move构造函数
  • 我猜
    makeMeat()
    创建类
    std::string
    的临时对象

    std::string temp("Pork");
    
    在这之后,
    std::string meat
    对象由
    temp
    对象中的数据创建并用复制构造函数初始化

    std::string meat(temp);
    
    最后,
    temp
    对象被销毁了吗

    我认为如果没有回报值优化,这种情况就会发生。
    如果是,会发生什么?

    字符串直接在
    meat
    中构造。不存在具有不同生命周期的临时变量。这就是所谓的省略

    这种行为在C++17下是强制性的,实际上,在任何合理的现代生产质量的现代编译器中都会发生,在C++03 11和14中没有设置病态的构建标志

    在C++14及更早版本中,类必须有一个move或copy-ctor才能执行上述操作,否则将导致生成中断。所述构造函数中的任何代码都不会运行

    古代或玩具编译器,或带有病态标志的编译器告诉他们不要省略,可能会组成2个临时对象,并与副本混在一起。这种情况并不有趣,因为病理编译器状态同样可以自由实现
    a+=b(具有
    a
    b
    积分类型)为
    for(i从0到b)+a!你应该诚实地认为缺乏精明同样是病态的。


    C++中的Eclipse是指对象生命周期和身份的标准允许合并。因此,在某种意义上,存在3个字符串(函数中的临时EIT、返回值和由返回值构造的值),它们的标识被合并到一个具有统一生存期的对象中。

    您可以使用自定义结构对此进行测试:

    struct S {
        S (const char *);
        S (S const&) = default;
        S (S&&) = default;
        virtual ~S();
    };
    
    S get_s () { return "S"; }
    
    int main () {
        S s = get_s();
    }
    
    如果没有选项,g++将省略大多数构造函数调用,此代码相当于:

    S s("S");
    
    因此,只调用
    constchar*
    中的构造函数

    现在,如果您告诉g++不要elide构造函数(
    -fno elide构造函数
    ),则有三个构造函数/析构函数调用:

  • 第一个创建一个临时的
    S(“S”)
  • 第二种方法是在
    get_s
    s(s&&)
    内部创建一个临时的
  • 然后调用第一个临时变量的析构函数
  • 然后在
    main
    内部调用move构造函数
  • 然后调用
    get\u s
    返回的临时文件的析构函数
  • 然后调用
    s
    的析构函数

  • 如果
    S
    没有移动构造函数,您可以简单地用上面列表中的复制构造函数替换移动构造函数。

    这对于您自己的测试非常简单。您不用使用
    std::string
    而是使用所需的构造函数和函数创建自己的类,这些构造函数和函数在调用时打印消息。然后你可以自己看看会发生什么。@JoachimPileborg在使用复制省略之类的功能时,我会说“自己试试”确实会给你一个答案,但不是一个反映所有必须发生的事情、可能发生的事情、需要可用的事情等的答案。不确定,但是:
    “Pork”
    不是类型为
    std::string
    的PR值,因此,编译器不需要在C++17中执行复制省略。@holt,但我们构造了一个类型为
    std::string
    的prvalue,从
    “Pork”
    返回,返回的是,否?不是绝对确定,但如果不是真的,我会很失望。@Holt:编译器从来都不是执行复制省略的“必需”编译器。只有“允许”这样做。@Nawaz我们说的是C++17,其中一些复制省略是必须的。@holt和我认为C到B也是如此。我应该再检查一遍。现在阅读…你能再解释一下第二步吗?它是否创建了一个实际“返回”到主流的对象?@vector2718在内存中有三个地方分配了
    S
    对象的存储:a)在主函数中,B)在
    get\u S
    的“调用堆栈”中,C)在
    get\u S
    的堆栈中。对象
    S(“S”)
    首先在
    C
    中构造,然后在返回位置
    B
    中移动,然后在其目标位置
    A
    中移动。我理解C)是get_S的堆栈框架,它存储此函数的所有参数和局部变量。但是你所说的get_s的“调用堆栈”是什么意思?@vector2718“调用堆栈”在这里可能是一个不好的术语-当你有一个返回某个东西的函数时,你需要将返回值存储在某个地方。根据调用约定,您可以将其存储在寄存器(例如使用
    \uu stdcall
    )或内存中的某个位置(在“调用堆栈”中)。