C++ 复制构造函数未调用,有人能解释函数返回值是如何工作的吗

C++ 复制构造函数未调用,有人能解释函数返回值是如何工作的吗,c++,c++11,return,return-value,C++,C++11,Return,Return Value,可能重复: 在此代码中,永远不会调用复制构造函数(移动构造函数或赋值运算符也不会调用)。这怎么可能?有人能解释一下函数如何返回值,堆栈和寄存器会发生什么情况(或者发布一些好的链接) #包括 #包括 使用名称空间std; 课堂测试; 测试getnew(int arg); 课堂测试 { 公众: 字符*conts; 内伦; 测试(char*input=NULL){conts=newchar[len=10]; if(输入)标准件(续,输入,9);else标准件(续,xxxxx); 使用时,T2被分配给

可能重复:

在此代码中,永远不会调用复制构造函数(移动构造函数或赋值运算符也不会调用)。这怎么可能?有人能解释一下函数如何返回值,堆栈和寄存器会发生什么情况(或者发布一些好的链接)

#包括
#包括
使用名称空间std;
课堂测试;
测试getnew(int arg);
课堂测试
{
公众:
字符*conts;
内伦;
测试(char*input=NULL){conts=newchar[len=10];
if(输入)标准件(续,输入,9);else标准件(续,xxxxx);
使用时,
T2
被分配给在
getnew
中初始化的
Test
对象。这就是RVO的工作原理。

理论上,编译器应该创建返回值的临时副本,然后将临时对象分配给接收函数输出的变量

但是,即使复制构造函数或移动构造函数有副作用,编译器也可以省略返回值的复制和移动。在您的情况下,这会变成所谓的NRVO(命名为返回值优化),这是RVO(返回值优化)的特例


因此,您看到的很可能是移动省略的结果。有一些编译器选项可以禁用此优化,但某些编译器(例如Clang 3.2)在处理这些选项时存在错误(请参见的答案)。

您说的“如何”是什么意思?我的意思是-幕后发生了什么:)快速简化解释有多少ABI处理返回“复杂”的函数按值分类。调用方负责提供返回值将存在的一些空间。然后它将指针作为额外参数传递给函数。函数在额外参数给定的地址构造其返回值。因此在getnew中,编译器拥有所有信息,可以在该ret处直接创建retjurn地址,跳过副本。就像成员函数的“this”指针一样?好的,如果这是真的,我现在对这个答案很满意,但是如果有一些关于函数返回值如何工作的好文章,那也不错。特别是在f(g())的情况下,g()是如何返回值并直接传递给f()的?h(调用
f(g())
的函数)为该临时变量保留一些空间,将指向该空间的指针传递给g,这样它就可以在那里构造返回值,并将相同的指针传递给f,这样它就可以读取该值。这里没有什么困难。请注意,无论f是按值取参数还是按常量取参数,都会发生相同的情况,只是按值得到的是非常量,因此建议你有时可以读到一些关于按值接受某些论点的修正。我想我已经写过了,我知道:)但是到底发生了什么?值的返回是如何工作的?你是在问程序集级别上发生了什么吗?我不太清楚。RVO中的重要一点是没有调用复制/移动构造函数。t2和retj将是相同的对象(相同的地址)。Test getnew(int)的实现可能与void getnew相同(Test*,int)。是的,如果可能的话,我想知道在汇编级别上发生了什么。编译器是否将t2的地址推送到retj应该位于的堆栈中?关于返回值如何工作的任何外部链接都是好的。我理解汇编的基本原理,所以我应该能够理解这些解释。提及“语义检查仍然需要可访问的复制构造函数/移动构造函数,即使编译器没有真正调用它们,否则代码格式错误“这是一个好主意。@纳瓦兹:好的观点,我同意这是相关的,但我认为问题更多地集中在代码编译时会发生什么。是的,这就是我想知道的。我知道编译器倾向于在可能的情况下不调用复制或移动构造函数。
    #include <iostream>
#include <cstring>

using namespace std;

class Test;

Test getnew(int arg);
class Test
{

  public:
    char *conts;
    int len;
        Test(char* input = NULL){conts = new char[len=10];
            if(input)strncpy(conts,input,9);else strcpy(conts,"xxxxx");
            cout << "\nconstructor: " << conts;
        };
        Test(const Test& t){
                conts = new char[10];
                if(t.len)strncpy(conts,t.conts,9);
                len = t.len;
                cout << "\ncopy-constructor: " << conts;
        };
        Test(Test&& t){
               conts = t.conts;
               t.conts = NULL;
               std::swap(len,t.len);
               cout << "\nmove-constructor: " << conts;
        };

        ~Test(){
            cout << "\ndestructor";
            if(conts)delete [] conts;
             len = 0;
             conts = NULL;
        };
        Test& operator=(Test rhs)
        {
            std::swap(conts,rhs.conts);
            std::swap(len,rhs.len);
            cout << "\nassigend: " << conts;
        }

};

int main()
{

   Test t2 = getnew(1);
   cout << endl << t2.conts;
    return 0;
}

Test getnew(int arg)
{
        Test retj("FFFFF");
        return retj;
}