C++编译器会优化未使用的返回值吗?

6
如果我有一个返回对象的函数,但是调用者从来没有使用过这个返回值,编译器会不会优化掉这个拷贝?(可能是总是/有时/从不的答案。)
基础示例:
ReturnValue MyClass::FunctionThatAltersMembersAndNeverFails()
{
    //Do stuff to members of MyClass that never fails
    return successfulResultObject;
}

void MyClass::DoWork()
{
    // Do some stuff
    FunctionThatAltersMembersAndNeverFails();
    // Do more stuff
}

在这种情况下,ReturnValue 对象是否会被复制?它甚至会被构造吗?(我知道这可能取决于编译器,但让我们将此讨论缩小到流行的现代编译器。)
编辑:让我们简化一下,因为在一般情况下似乎没有共识。如果 ReturnValue 是一个 int,并且我们返回 0 而不是 successfulResultObject 呢?
7个回答

3

如果ReturnValue类具有非平凡的复制构造函数,则编译器不得消除对复制构造函数的调用,因为语言规定必须调用它。

如果复制构造函数是内联的,则编译器可能能够内联调用,进而可能导致其代码的大部分被消除(这也取决于FunctionThatAltersMembersAndNeverFails是否是内联的)。


4
不是这样的。针对临时对象的特定情况,编译器有明确的许可,可以直接在目标位置构造该对象,而不是复制它(参见ISO 14882 §12.2)。如果中间对象有名称,那么你是正确的。 - puetzk
在给定的示例中,如果successfulResultObject的类型已经是ReturnValue,那么它如何不调用复制构造函数?在这种特殊情况下,“直接复制”仍涉及复制构造函数。 - Martin v. Löwis
2
编译器不能消除对复制构造函数的调用,这个信息似乎是错误的,否则NRVO就不可能存在了,不是吗? - Martin Ba

1
如果优化级别导致内联代码,它们很可能会这样做。如果不能内联,它们就必须生成两个不同的代码翻译来使其工作,这可能会引发很多边缘案例问题。

1
可以处理这种情况,即使原始的调用者和被调用者在不同的编译单元中。 <如果您有充分理由关注方法调用所需的CPU负载(过度优化是万恶之源),则可以考虑许多可用于内联的选项,包括(惊喜!)宏。

您真的需要在这个级别上进行优化吗?


连接器无法进行任何优化。被调用的函数无法知道结果是否被使用,因此必须生成它。只有编译器拥有足够的知识来接近优化这一点,而这只会在所有内容都被内联时发生。 - Martin York
1
我认为他可能在谈论像微软的链接器这样的工具,它会获取所有对象的中间表示,并执行另一个编译步骤以进行模块间优化。 - Zan Lynx
@ZanLynx 不仅仅是微软,GCC也有非常成熟的链接时优化功能。打开这个功能意味着编译器可见的任何内容现在都对链接器可见(以中间格式流式传输),可以跨越翻译单元边界进行内联或其他优化,通常会产生惊人的效果。我认为LLVM / Clang和其他现代编译器也适用于此,并与竞争保持同步。 - underscore_d

1

如果返回值是int并且你返回0(就像编辑后的问题中一样),那么这个可能会被优化掉。

你需要查看底层汇编代码。如果函数没有内联,那么底层汇编将执行mov eax, 0(或xor eax,eax)来将eax(通常用于整数返回值)设置为0。如果函数被内联,那么这肯定会被优化掉。

但是,如果你担心返回大于32位的对象时会发生什么,那么这种情况并不太有用。你需要参考未编辑问题的答案,这些答案描述得非常清楚:如果所有内容都被内联了,那么大部分内容都会被优化掉。如果没有内联,则必须调用函数,即使它们实际上什么也没做,包括对象的构造函数(因为编译器不知道构造函数是否修改了全局变量或执行了其他奇怪的操作)。


0

如果它们位于不同的编译对象(即不同的文件)中,则大多数编译器可能无法执行此操作。如果它们都位于同一文件中,则可能可以。


同意。启用“整个程序优化”也可能会发生这种情况,前提是在任何调用中都没有使用返回值。 - Drew Hall
@Drew,“整个程序优化”是vc++的东西吗?因为我无法想象在Linux/Unix环境中可能实现。 - Paul Tomblin
关于此事的努力的传言,例如在这里:http://www.airs.com/blog/archives/100 - Steve Jessop
@Paul,这是可能的,但你需要告诉GCC编译所有源文件并使用-fwhole-program标志。它很容易使用1.5 GB及以上的内存。 - Zan Lynx

0

有很大的可能性,一个窥孔优化器会捕捉到这个问题。许多(大多数?)编译器都实现了这个功能,所以答案可能是“是的”。

正如其他人所指出的,在AST重写层面上,这不是一个简单的问题。


Peephole优化器在汇编语言级别(但在生成实际机器代码之前)的代码表示上工作。有可能注意到将返回值加载到寄存器中,然后进行无中间读取的覆盖,并且只需删除加载操作。这是基于具体情况逐个处理的。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接