【问题标题】:Visual C++ __forceinline strange behaviorVisual C++ __forceinline 奇怪的行为
【发布时间】:2016-12-12 13:52:43
【问题描述】:

我正在尝试强制 Visual c++ 编译器内联特定函数。我知道inline 或__forceinline 只是一个建议。根据MSDN,如果编译器不能内联使用__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 修饰符。

【问题讨论】:

  • 可能是警告默认关闭而您没有启用它?
  • @sharptooth,不,警告级别 3 已打开,并且将警告视为错误已打开。刚刚检查过。
  • 无论您拥有哪种警告级别,某些警告默认为“关闭”。
  • @Bo Persson,我正在使用 /Ob2 选项进行编译。也尝试使用 /Ob1 - 结果相同
  • @sharptooth,如何启用此特定警告?

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


【解决方案1】:

同一警告有 2 个版本:

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

编译器警告(1 级)C4714
Visual Studio 6.0
'function' : 标记为 __forceinline 的函数未内联

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

编译器警告(4 级)C4714
Visual Studio 2015
函数 'function' 标记为 __forceinline 未内联

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

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

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

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。 `std::string` 在 __forceinline 函数中按值返回

这个真的很奇怪,不过好像有什么干扰 __forceinline-ed 函数介于 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;
}

不用说所有 3 个版本都可以通过使用 inline 关键字而不是 __forceinline 来解决。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-03
    相关资源
    最近更新 更多