Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/157.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在C++;复制省略?_C++_C++11_Copy Elision - Fatal编程技术网

C++ 在C++;复制省略?

C++ 在C++;复制省略?,c++,c++11,copy-elision,C++,C++11,Copy Elision,据我所知,由于拷贝省略或返回值优化,只创建了一个a实例,而不是a a和a b的两个实例。 但从内存的角度来看上述程序,对象a位于函数beta的堆栈激活记录或堆栈空间内,内存位置为0x7ffc12bdaf77。当它从测试版返回时,复制省略使对象b与对象a完全相同,而不是复制它。因此,现在b也有地址0x7ffc12bdaf77。我无法理解,如果b仍然是函数main的一个本地函数,并且存在于其堆栈空间内,它如何能够占用main堆栈空间外的内存?在microsoft x64调用约定中,隐藏指针将作为be

据我所知,由于拷贝省略或返回值优化,只创建了一个a实例,而不是a aa b的两个实例。
但从内存的角度来看上述程序,对象a位于函数beta的堆栈激活记录或堆栈空间内,内存位置为0x7ffc12bdaf77。当它从测试版返回时,复制省略使对象b与对象a完全相同,而不是复制它。因此,现在b也有地址0x7ffc12bdaf77。我无法理解,如果b仍然是函数main的一个本地函数,并且存在于其堆栈空间内,它如何能够占用main堆栈空间外的内存?

在microsoft x64调用约定中,隐藏指针将作为
beta
的第一个参数。此指针包含
b
的地址,该地址将位于
main
的堆栈框架上。此指针将用于同时在
main
的堆栈帧上立即创建
a
b
。所以换句话说,
a
beta
的堆栈框架中的任何地方都不存在;从内存角度来看,
a
b
将是等效的

据我所知,只创建了a的一个实例,而不是2个

它如何占据main堆栈空间之外的内存

没有


返回值只是在
main
调用的堆栈框架中创建的。

考虑这一行:
ab=beta()。编译器是如何实现的

嗯,
beta
是另一种功能。而
beta
的返回值为
a
。所以有两种可能的方法来实现这一点。编译器可以使
beta
为其
A
返回值分配堆栈空间,但这可能会有问题,因为调用者需要使用该返回值。因此,编译器让调用方为返回值分配堆栈空间。毕竟,调用方确实知道返回值的大小/对齐方式,因此它拥有分配该空间所需的一切

那么,让我们选择后者。这意味着当编译器调用
beta
时,它会传入
beta
返回值所在的地址。但是这也意味着编译器,对于这个特定的
beta
调用,可以给
beta
的返回值提供与它将给
b
相同的地址

在这里,我们将函数返回值中的副本省略为
b

因此,当编译器开始编译
beta
时,它知道调用者将给它一个指针,指向返回值应该去的地方。因此
返回a
语句从
变量语义上复制到此返回值对象中

但是,编译器可以看到整个
beta
。它可以看到
a
变量是一个局部变量,它在所有控制路径上返回。因此,编译器可以将
a
放在调用者为返回值提供的内存中,而不是给
a
一个单独的堆栈地址


因此,我们再次将
a
中的一个副本省略到返回值中。

这让我感到惊讶。只能看到函数声明的编译器如何知道这个隐藏指针?还是总是有一个指针传递给可选的复制省略返回值的填充?@swithwings:因为编译器就是这样编译返回值(或者更确切地说是一个太大或太复杂而无法放入寄存器的值)的函数的。这是调用约定的一部分,完全由函数的声明决定。@Nicolas好的,那么这是ABI在所有情况下如何实现返回值的内置特性?有道理。我很困惑,因为在C++17之前,NRVO是一个“优化”,因此您通常需要函数定义来知道它是否可以完成(即使这样,它“可能”还没有完成)。但是,经过进一步思考,我想总的来说,没有任何其他方法可以实现这一点?正是因为这个原因DIT出现在我之后,C++ 17“保证删除”规则要求这样做,因此每个C++ ABI必须这样工作,而不仅仅是MS的X64调用约定。(因为我不知道在任何主流播放窗体上C++ 17的任何ABI中断)@ AtasoRessWuffEng:记住,根据C++,有两个副本:从返回到返回值对象的拷贝,以及从返回值对象到接收端堆栈变量的拷贝。函数的实现可以省略其中一个,而接收方可以省略另一个。如果两者都被省略,那么就存在完全省略。
#include <iostream>

using namespace std;

class A
{
public :
    A()
    {
        cout<<"constructor is called"<<endl;
    }
    
    ~A()
    {
        cout<<"destructor is called"<<endl;
    }
    
    A(const A &s)
    {
        cout<<"copy constructor is called"<<endl;
    }
};

A beta()
{
    A a;
    cout<<"mem location a : "<<&a<<endl;
    return a;
}

int main(int argc, char** argv) {
    A b = beta();
    cout<<"mem location b : "<<&b<<endl;
    return 0;
}
constructor is called
mem location a : 0x7ffc12bdaf77
mem location b : 0x7ffc12bdaf77
destructor is called