为什么编译器会在二进制文件中留下内联函数的实现?

Why would the compiler leave in the binary the implementation of an inlined function?

考虑这段简单的代码:

#include <iostream>
#include <sstream>

class Utl
{
public:

    // singleton accessor
    static Utl& GetInstance();

    virtual std::string GetHello( bool full ) = 0;
};

class UtlImpl : public Utl
{
public:
    UtlImpl() {}

    virtual std::string GetHello( bool full )
    {
        return (full)?"full":"partial";
    }
};

Utl& Utl::GetInstance()
{
    static UtlImpl instance;
    return instance;
}

int main( int argc, char* argv[] )
{
    std::cout << Utl::GetInstance().GetHello(true) << std::endl;  
    std::cout << Utl::GetInstance().GetHello(false) << std::endl;
    return 0;
}

我用 Visual Studio 2015 在 "Debug" 和 "RelWithDebInfo" 模式下编译。

然后我使用覆盖率验证工具(Software verify - Coverage Validator)。

对于 Debug 构建,该工具报告 100% 的覆盖率

对于 RelWithDebInfo 构建,该工具报告了 66.67% 的覆盖率。它报告函数 Utl::GetInstance() 尚未执行。

由于 RelWithDebInfo 已优化,我怀疑这是因为该函数已被编译器内联(我不熟悉汇编代码来验证这一点,但我可以 post 如果有人向我解释如何检查这个,任何有帮助的东西)。但是当我使用 Software verify 的 DbgHelp 浏览器工具时,这个工具报告二进制文件中存在 Utl::GetInstance()

是否有可能 Visual Studio 内联了 Utl::GetInstance() 函数的代码,但还在二进制文件中保留了 "real" Utl::GetInstance() (然后可能以这个的两个实现结束代码)?这可以解释为什么该工具报告我该函数从未被调用,而它的代码肯定已被执行...

任何具有全局范围的函数都需要有一个可调用函数和内联函数,所以会有重复。

在构建时将 "Inline function expansion" 设置为 "Disabled (/Ob0)" 允许 OP 获得 100% 的测试覆盖率。