【问题标题】:Benefits of inline functions in templates模板中内联函数的好处
【发布时间】:2014-05-21 16:50:04
【问题描述】:

根据标准,C++ 中成员函数的内联定义与使用inline 关键字声明它是一样的。请记住,我编写的大多数模板只包含内联函数,因为这样代码更短(函数的主体也必须在标题中,以便可以从多个 .cpp 文件中使用,因此它可以说只会减慢解析速度)。

现在(健全的)编译器是否会尝试内联所有函数?在实践中将内联成员函数定义为inline 有什么好处(见下面的例子)?从现在开始我是否应该避免在模板中编写内联函数?

template <class Foo>
struct Bar {
    inline bool IsThereBenefitInDoingThis()
    {
        return 10 < system("exit `wget -q -o - "
            "http://stackoverflow.com/questions/23789353/ | grep yes | wc -l`");
    }
};

【问题讨论】:

  • 类定义中定义的任何函数都是内联的,隐式的。
  • 当然,如果可以避免的话,您希望将函数定义放在类定义中。它实际上破坏了类的可读性。 (在大多数情况下,如果可以避免的话,您甚至不希望它们在同一个文件中。)
  • @JamesKanze 是的,我知道将函数体放在 .hpp 文件中并将 #includeing 放在原始标题中的做法。在这种情况下,问题更多是关于编译器是否真的遵守标准。
  • 许多编译器会执行内联的成本/收益分析,也许考虑到用户使用了inline。但是inline 不强制内联。来自 C++11 标准,[dcl.fct.spec]/3 " inline 说明符向实现表明,在调用点对函数体进行内联替换优于通常的函数调用机制。一个实现不需要在调用点执行此内联替换 [...]"
  • (Stepanov 在他的一次演讲中说,在某些情况下帮助编译器仍然有用。如果有疑问,请测量。)

标签: c++ templates inline


【解决方案1】:

一个简单的测试:

#include <math.h>
#include <stdio.h>

template <class T>
class Sine {
public:
#ifdef MAKEINLINE
    inline T Eval(T x)
#else // MAKEINLINE
    T Eval(T x)
#endif // MAKEINLINE
#ifdef PUTOUTSIDE
    ;
#else // PUTOUTSIDE
    {
        return T(sin(double(x)));
    }
#endif // PUTOUTSIDE
};

#ifdef PUTOUTSIDE
template <class T>
#ifdef MAKEINLINE
inline T Sine<T>::Eval(T x)
#else // MAKEINLINE
T Sine<T>::Eval(T x)
#endif // MAKEINLINE
{
    return T(sin(double(x)));
}
#endif // PUTOUTSIDE

int main(int argc, const char **argv)
{
    printf("%g\n", Sine<float>().Eval(M_PI));
    printf("%g\n", Sine<double>().Eval(M_PI));
    return 0;
}

跑步:

g++ Main.cpp -o testi -DMAKEINLINE -O1
g++ Main.cpp -o testn -O1
g++ Main.cpp -o testo -DPUTOUTSIDE -O1
g++ Main.cpp -o testoi -DMAKEINLINE -DPUTOUTSIDE -O1
diff testi testn
diff testi testo
diff testi testoi

揭示了testitesto 之间的区别,所以只要主体在内部,它是否被声明为inline 似乎并不重要,或者至少在这个简单的情况下和在 g++ (GCC) 中4.6.4.请注意,提高优化级别会内联该函数的所有版本,并且没有区别。

【讨论】:

    【解决方案2】:

    您应该假设出于优化目的,编译器会忽略 inline 关键字,并自行决定内联的内容,当您将内联形式用于类成员函数时,同样的规则也适用。我不确切知道哪些编译器在什么条件下会或不这样做,但标准的说法是这是一般规则,并且基于观看在线编译器课程的视频,我相信它。

    但是,这并不完全意味着“内联”关键字或内联成员函数形式没有用处,或者没有任何后果。关键问题是在头文件中包含相关定义是否有效。非内联定义不应在头文件中 - 这样做可能会导致链接器错误(重复符号,其中定义被重复编译为多个 cpp 文件的一部分)。

    因此,如果您希望在头文件中定义定义,则需要一种或另一种形式的内联(结果是编译时间更长,因为定义在多个 cpp 文件中编译,除了一个版本之外的所有版本都被链接器丢弃)。

    也就是说,模板又有点不同了。我忘记了细节,但我认为默认情况下,模板的处理方式类似于inline 定义 - 在头文件中完全定义模板当然是有效的(并且通常是必要的)。

    【讨论】:

    • 虽然没有真正回答这个问题,但我在问内联 inline 模板函数(在正文中定义并同时声明 inline 的模板函数)与其他两种编写内联函数的方法。
    • @the swine - 是的,对不起,我想不,但我真的不知道。你在你的答案中的测试是(正如你在最后所说的)暗示性但不是确定性的。
    猜你喜欢
    • 2013-07-14
    • 1970-01-01
    • 2019-02-11
    • 2011-10-29
    • 2014-02-08
    • 2014-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多