复制省略和返回值优化相对于复制构造函数

Copy elision and return value optimization versus copy constructor

本文关键字:相对于 复制 构造函数 优化 返回值 复制省      更新时间:2023-10-16

我一直在读关于复制省略和返回值优化如何通过避免调用对象复制构造函数来提高速度的文章。我理解这些机制是如何工作的,但我想知道这是否会导致程序的行为与人们预期的不一样。

本质上,我的问题是;如果我们编写复制构造函数而不创建作为另一个对象副本的对象,会发生什么?换句话说,如果

AClass original;
AClass copy ( original );
// copy == original -> false

例如,我们有一个这样的类:

// AClass.hpp
struct AClass
{
    static int copyCount;
    int copyNumber;
    AClass():copyNumber(0){}
    AClass( AClass const& original ):copyNumber(++copyCount){} // I think this is the signature for the copy constructor
};

// AClass.cpp
int AClass::count ( 0 );

这显然是一种可怕的行为,我并不是说我会做这样的事。然而,这一点是站得住脚的;如果我们依赖复制的副作用呢?在本例中,跟踪我们制作了多少份副本。我希望优化不会影响程序的运行方式。但是,复制省略可能会导致以下代码失败:

// Main.cpp
AClass MakeAClass()
{
    return AClass();
}
int main()
{
    AClass copy ( MakeAClass() );
    if ( AClass::copyCount == 1 )
    {
        return 0;
    }
    else
    {
        return -1;
    }
}

当我在没有优化的调试模式下构建时,这可能会返回0,但当我打开优化并且MakeAClass的返回直接放在副本上,跳过副本构造函数时,它会突然失败。

当编译器尝试这些优化以寻找副作用时,是否进行了检查?当您要求复制时,期望代码执行复制是错误的吗?

是。如果复制构造函数(或移动构造函数或析构函数)有副作用,则复制省略可以更改代码的行为。

这就是重点。如果它不能改变行为,就没有理由在标准中提及它。不改变行为的优化已经包含在假设规则中。(1.9/1)即:

本国际标准中的语义描述定义了参数化的不确定性抽象机。这个国际标准对一致性的结构没有要求实现。特别是,他们不需要复制或模仿抽象机器的结构。相反,符合的实现被要求(仅)模仿抽象的可观察行为机器如下所述。

复制省略在标准中被明确提及,正是因为它可能违反此规则。