Visual C ++ __forceinline奇怪的行为

时间:2016-12-12 13:52:44

标签: c++ visual-c++ inline

我试图强制使用visual c ++编译器来内联特定的函数。我知道inline__forceinline只是一个建议。根据{{​​3}},如果编译器无法内联使用__forceinline关键字标记的函数,则会生成警告:

  

如果编译器无法内联用__forceinline声明的函数,   它会产生1级警告。

但我没有得到这样的警告。当我调试我的应用程序时,我发现在反汇编窗口中有一个call <inlined function address>,即我的函数没有内联。那我怎么检查我的功能是否内联?这是否意味着如果没有生成警告,那么函数被内联并且问题出现在其他地方?

这是我的功能:

__forceinline SecByteBlock aes_generate_iv(size_t blockSize)
{
    const int seedSize = 32;
    SecByteBlock rngSeed(seedSize);
    OS_GenerateRandomBlock(false, rngSeed, rngSeed.size());

    RandomPool rngp;
    rngp.IncorporateEntropy(rngSeed, rngSeed.size());

    SecByteBlock aesIV(blockSize);
    rngp.GenerateBlock(aesIV, aesIV.size());

    return aesIV;
}

以下情况是VC ++无法内联函数(来自MSDN):

  • 使用/ Ob0(调试版本的默认选项)编译函数或其调用者。
  • 函数和调用者使用不同类型的异常处理(一个是C ++异常处理,另一个是结构化异常处理)。
  • 该函数有一个可变参数列表。
  • 除非使用/ Og,/ Ox,/ O1或/ O2编译,否则该函数使用内联汇编。
  • 该函数是递归的,不附带#pragma inline_recursion(on)。使用pragma,递归函数被内联到16个调用的默认深度。要减少内联深度,请使用inline_depth pragma。
  • 该功能是虚拟的,虚拟调用。可以内联直接调用虚函数。
  • 程序获取函数的地址,并通过指向函数的指针进行调用。可以内联直接调用已经获取其地址的函数。
  • 该函数也标有裸__declspec修饰符。

1 个答案:

答案 0 :(得分:0)

同一警告有两个版本:

https://msdn.microsoft.com/en-us/library/aa734011(v=vs.60).aspx

编译器警告(1级)C4714
Visual Studio 6.0
&#39;功能&#39; :标记为__forceinline的函数未内联

https://msdn.microsoft.com/en-us/library/a98sb923.aspx

编译器警告(4级)C4714
Visual Studio 2015
功能&#39;功能&#39;标记为__forceinline未内联

因此,要启用警告,例如,在Visual Studio 2015中,您必须默认使用级别4(/ W4)而不是级别3(/ W3)。

以下有关如何消除此类编译器行为的建议。 我在MSVC2015中解决了这些案例:

  1. 递归调用
  2. SomeType SomeType::operator++(int) / SomeType SomeType::operator--(int)
  3. std::string在__forceinline函数
  4. 中按值返回

    1。递归调用

    template <typename T>
    __forceinline T int_log2_floor(T v)
    {
        ASSERT_GT(v, T(0));
    
        if (1 >= v) return 0;
    
        return T((v >= 2 ? int_log2_floor(v / 2) : 0) + 1); // Warning C4714
    }
    

    这里的一个解决方案就是展开递归:

    template <typename T>
    __forceinline T int_log2_floor(T v)
    {
        ASSERT_GT(v, T(0));
    
        if (1 >= v) return 0;
    
        T ret = 0;
        T i = v;
    
        do {
            ++ret;
            i /= 2;
        } while (i >= 2);
    
        return ret;
    }
    

    2。 `SomeType SomeType :: operator ++(int)`/`SomeType SomeType :: operator - (int)`

    class SomeType
    {
        __forceinline SomeType operator++(int) // Warning C4714
        {
            const auto it = *this;
            m_it++;
            return it;
        }
    
        //...
        iterator m_it
    };
    

    这个对我有用:

    class SomeType;
    
    SomeType operator++(SomeType & r, int);
    
    class SomeType
    {
        friend SomeType operator++(SomeType & r, int);
    
        //...
        iterator m_it
    };
    
    __forceinline SomeType operator++(SomeType & r, int)
    {
        const auto it = r;
        r.m_it++;
        return it;
    
        // You still can write here `const auto it = *this; m_it++; return it`,
        // but i hell don't know why this works.
    }
    

    3。在__forceinline函数

    中返回的`std :: string`

    这个真的很奇怪,但似乎之间存在某种干扰 __forceinline - inline - ed函数之间的std::string个函数,因为inline基本上使用第二个函数 - template<typename T> __forceinline std::string int_to_bin(T i, bool first_bit_is_lowest_bit = false) // Warning C4714 { std::bitset<sizeof(T) * CHAR_BIT> bs(i); if (!first_bit_is_lowest_bit) { return bs.to_string(); } const std::string bs_str = bs.to_string(); return std::string(bs_str.rbegin(), bs_str.rend()); }

    inline

    由于警告是由按值返回类型触发的,因此此处的解决方案是通过参数返回并通过template<typename T> __forceinline void int_to_bin_forceinline(std::string & ret, T i, bool first_bit_is_lowest_bit = false) { std::bitset<sizeof(T) * CHAR_BIT> bs(i); if (!first_bit_is_lowest_bit) { ret = bs.to_string(); return; } const std::string bs_str = bs.to_string(); ret = std::string(bs_str.rbegin(), bs_str.rend()); return; } template<typename T> inline std::string int_to_bin(T i, bool first_bit_is_lowest_bit = false) { std::string res; int_to_bin_forceinline(res, i, first_bit_is_lowest_bit); return res; } 内联:

    inline

    毋庸置疑,所有3个版本都可以通过__forceinline关键字用法而不是doesRelativeDateFormatting来解决。