C++ 传递或返回变量时并不总是调用复制构造函数

C++ 传递或返回变量时并不总是调用复制构造函数,c++,constructor,copy-constructor,C++,Constructor,Copy Constructor,作为回答,有人提到,当按值将变量传递到函数中或作为函数的返回值时,不一定要调用复制构造函数。有人能解释一下这是什么时候发生的以及为什么吗?在这种情况下,编译器如何返回结果?如前所述,即and 当新创建并复制对象时,在传递时可能会发生这种情况。在这种情况下,允许编译器对此进行优化,以便在正确的位置直接创建新对象,并且不需要复制(并且不会调用复制构造函数) 例如: struct A {}; void test(A a) {} int main() { test(A()); // probab

作为回答,有人提到,当按值将变量传递到函数中或作为函数的返回值时,不一定要调用复制构造函数。有人能解释一下这是什么时候发生的以及为什么吗?在这种情况下,编译器如何返回结果?

如前所述,即and

当新创建并复制对象时,在传递时可能会发生这种情况。在这种情况下,允许编译器对此进行优化,以便在正确的位置直接创建新对象,并且不需要复制(并且不会调用复制构造函数)

例如:

struct A {};
void test(A a) {}

int main() {
   test(A()); // probably there will be no copy here
}
A returnANewA() {
    return A(); // copying would take place here
}

int main() {
    A a = returnANewA(); // the compiler is allowed to do that without copying
}
对于返回,这是类似的。您创建一个新对象,然后如果返回它,这将涉及一个副本,但允许编译器优化该副本(以及对副本构造函数的调用)

例如:

struct A {};
void test(A a) {}

int main() {
   test(A()); // probably there will be no copy here
}
A returnANewA() {
    return A(); // copying would take place here
}

int main() {
    A a = returnANewA(); // the compiler is allowed to do that without copying
}
编译器是如何做到这一点的:根据调用约定,它知道返回值必须存储在堆栈上的什么位置。在其他情况下,如果编译器知道函数代码,它当然会帮助编译器。但这一切都取决于体系结构(x86或其他)和编译器(GCC、Microsoft或其他)。标准只是说允许编译器省略对复制构造函数的调用


如果您对有关调用约定的一些依赖于平台的详细信息感兴趣,这里有一些链接。但是请注意,这些细节其实并不重要。您所需要知道的是,编译器可以优化复制构造函数调用(在大多数情况下都会这样做)


阅读。@Nawaz和按值传递如何?这不仅仅是按值返回。@MartinDrozdik如果编译器看到/知道值不会被修改,它可能会选择按addressSee传递。@MartinDrozdik编译器可以做两件事:1)遵循“好像”规则,即只要观察到的行为没有改变,就做它想做的事情。您的
const
参考示例可能违反此要求,也可能不违反此要求。2) Elide副本,如上链接所述。这是允许打破“似乎”规则的。如果按值获取参数,编译器可能会删除副本,而只是就地构造对象。RVO也是如此。什么是“正确的地方”?在较低的级别上,函数调用是否实际包含一个隐藏参数,即调用方希望存储结果的地址?如果是这样,这很容易理解。但是我猜有一些特殊情况-当返回
int
时,我猜函数只是将值留在堆栈顶部?@AaronMcDaid:不,没有隐藏参数。但根据调用类型,它知道返回值必须存储在堆栈上的什么位置。在其他情况下,如果编译器知道函数代码,它当然会帮助编译器。但这一切都取决于体系结构和编译器。标准只是说允许编译器省略对构造函数的调用。“它知道返回值必须存储在哪里”。它怎么能“知道”?:-)一种选择是,它总是存储在堆栈的顶部(或与顶部的固定偏移)。或者,我发现有趣的“在内存中传递”,调用者分配内存并将指向它的指针作为隐藏的第一个参数传递;被调用者填充内存并返回指针,返回时弹出隐藏的指针。“被调用者“知道”,因为调用者已将指向该位置的指针作为隐藏参数放置。。。。就这句话而言,我认为“通过”意味着“返回”。这是被调用方(函数)将结果传递回调用方的方式。调用约定在编译时是固定的,函数是根据调用约定编译的。因此,它总是将返回值放在定义良好的位置(可以通过隐藏参数定义)。这确实很有趣——我并不知道关于特定x86调用约定的所有细节。但要理解这种特定的优化,也没什么大不了的,只是它可以优化复制构造函数调用。