定义内联函数的正确方法是什么?

时间:2013-08-13 06:43:16

标签: c++ inline

假设我有一个X(X.h)类:

class X {
  private:
    unsigned unitsSold = 0;
    double revenue = 0.0;

  public:
    double avgPrice();
}

应该{​​{1}}定义哪种方式?

Option 1(课堂内):

X.h

avgPrice()

Option 2(与类相同的文件,但在类定义之外):

X.h

class X {
  private:
    unsigned unitsSold = 0;
    double revenue = 0.0;

  public:
    double avgPrice() {
      return unitsSold ? revenue / unitsSold : 0;
    }
}

Option 3(在单独的头文件中):

X.h:

class X {
  private:
    unsigned unitsSold = 0;
    double revenue = 0.0;

  public:
    double avgPrice();
}

inline double X::avgPrice() {
  return unitsSold ? revenue / unitsSold : 0;
}

X-inl.h:

class X {
  private:
    unsigned unitsSold = 0;
    double revenue = 0.0;

  public:
    double avgPrice();
}

6 个答案:

答案 0 :(得分:6)

inline说明符的含义可能存在一些误解。是的,它确实向编译器提示,最好是内联代码而不是进行调用,但编译器不必强制遵循此提示。 inline说明符的主要用途是避免违反One Definition Rule

声明函数inline后,需要在它使用的每个转换单元中定义它,并且每次定义必须完全相同。这是另一种方式,而不是你的标题所暗示的 - 你定义函数的位置选择是否需要标记inline

1)和2)没关系。在第一种情况下,它是隐式inline,在第二种情况下,您明确地声明了它。无论您何时包含标题,定义都是相同的。

案例3)仅在编译并链接X_impl.h作为源文件时才有效。在这种情况下,只有一个定义,inline将是多余的。这样,编译器在其他翻译单元中看不到定义,这使得它无法内联函数,无论它是否为inline

如果X_impl.h的意图是减少标题的视觉大小,那么您应该反过来,将其包含在X.h的末尾。 inline必须保持原状。

答案 1 :(得分:1)

这三个选项都是纠正。

取决于这样的事情:

  • 如果你的函数很短(比如getters / setters),那么在类定义中直接看到函数就更常见了。

  • 如果您的函数较大,最好在另一个标题中定义它,并且在使用该函数的源中仅包含此标题。这只会加快你的编译速度。但inline大功能很少见。

但不要忘记,这不是因为你使用了inline关键字,你的编译将内联你的函数。由编译器决定它是否会在它使用的每个地方使用该功能。

标准中明确说明了这一点:

  

7.1.2 / 2函数说明符[dcl.fct.spec]

     

带有inline说明符的函数声明声明了一个内联函数。内联说明符向实现指示在调用点处函数体的内联替换优先于通常的函数调用机制。 在通话点执行此内联替换不需要实现;但是,即使省略了这种内联替换,仍应遵守7.1.2定义的内联函数的其他规则。

最后一件事:

  

大多数编译器已经优化了代码,以便在更方便时生成内联函数。此说明符仅指示内联首选此编译器的编译器。


正如jrok所说,inline主要用于避免违反一个定义规则。在这里,我们还可以引用标准的一小部分:

  

(§7.1.2/ 4)内联函数应在 odr-used 的每个翻译单元中定义,并且应具有完全相同的定义。每个案例(3.2)。

     

3.2一个定义规则[basic.def.odr]

     

任何翻译单元都不得包含任何变量,函数,类类型,枚举类型或模板的多个定义。

答案 2 :(得分:1)

我会选择每个有利于可读性的方法,这取决于函数的大小:

  • 一行功能 - >选项1,
  • 小尺寸功能 - >选项2,
  • 中等大小的功能 - >选项3,
  • 大尺寸功能 - >你确定要内联吗?

如果您有大量的小尺寸功能,请选择选项3,不要将选项2和3混合在一起。

此外,当您提交第三个选项时,您必须记住包含X-inl.h而不是X.h。如果修改如下:

X.h:

#ifndef _CLASS_X_H_
#define _CLASS_X_H_
class X {
  private:
    unsigned unitsSold = 0;
    double revenue = 0.0;

  public:
    double avgPrice();
};
#include "X-inl.h"
#endif

X-inl.h:

inline double X::avgPrice() {
  return unitsSold ? revenue / unitsSold : 0;
}

然后您可以像往常一样加入X.h

答案 3 :(得分:0)

这取决于您使用内联函数的位置以及使用频率的频率。如果内联函数的代码很短(就像对于大多数getter / setter一样)并且如果它(很可能)在很多地方使用,那么将它直接放入类定义中是一种直截了当的方法。

如果您的内联函数是“大量”并且仅由您班级的少数用户使用,则将其置于单独的标题中是最佳选择。在这些情况下,这会加速编译,不需要内联,但需要您将额外的头文件传递给lib的用户。

答案 4 :(得分:0)

我认为inline关键字和内联函数/方法可能存在一些混淆。

关键字inline告诉编译器它应该将函数/方法代码复制到调用函数/方法的位置。例如:

/* [...] */
inline void sayHello() {
    std::cout << "Hello" << std::endl;
}

int main(int argc, char **argv) {
    sayHello();
    return 0;
}

将成为

/* [...] */
int main(int argc, char **argv) {
    std::cout << "Hello" << std::endl;
    return 0;
}

编译时。但编译器不必强制内联函数/方法。

另一方面,内联方法在您声明它的位置实现。例如:

/* [...] */
class X {
private:
    unsigned int unitsSold;
    double revenue;

public:
    /* [...] */
    double avgPrice() {
        if (unitsSold == 0) {
            return 0.0;
        }
        return revenue/unitsSold;
    }
};

答案 5 :(得分:-1)

只要将inline建议添加到编译器中,您提供的三个选项(必须)将导致相同的编译代码。

这总是考虑标准编译器执行标准编译任务......