4

Say I have a class X (X.h):

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

  public:
    double avgPrice();
}

Which way should avgPrice() be defined?

Option 1 (in-class):

X.h

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

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

Option 2 (in same file as the class, but outside of the class definition):

X.h

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

  public:
    double avgPrice();
}

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

or Option 3 (in a seperate header file):

X.h:

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

  public:
    double avgPrice();
}

X-inl.h:

#include "X.h"

inline double X::avgPrice() {
  return unitsSold ? revenue / unitsSold : 0;
}
Community
  • 1
  • 1
Timo Türschmann
  • 4,388
  • 1
  • 22
  • 30
  • Are you looking for technical correctness or best practices ? The usage of *correct* seems to refer to the former, however given that you only presented technically correct alternatives it seems to me that you are actually asking for the latter. – Matthieu M. Aug 13 '13 at 07:21
  • What about option 4? In `X.cpp` where it belongs. – GManNickG Aug 13 '13 at 07:27
  • 1
    @GManNickG The question is about *inline* functions. You're not suggesting putting *inline* functions into the .cpp file, are you? – Angew is no longer proud of SO Aug 13 '13 at 07:54
  • @Angew: I am actually. The linker can inline it just fine. – GManNickG Aug 13 '13 at 13:50
  • @GManNickG The linker can do anything it wants to inline and non-inline functions alike, but in a legal C++ program, "An inline function shall be defined in every translation unit in which it is odr-used and shall have exactly the same definition in every case." (quoting `[dcl.fct.spec]p4` of C++11 draft N3242). – Angew is no longer proud of SO Aug 13 '13 at 14:28
  • @Angew: I know what an inline function. My point is that OP should just stick it in a .cpp, non-inline, like any other regular function. Unless OP wants to keep this code header-only, there's no reason to leave the implementation in the header. (And perhaps I'm wrong, but I seriously doubt OP is doing this for any reason other than some belief that the inline function will be "faster", hence my comment on the linker being able to inline function.) – GManNickG Aug 13 '13 at 14:36

6 Answers6

6

There might be some misunderstanding about the meaning of inline specifier. Yes, it does give a hint to the compiler that it would be preferred to inline the code instead of making a call, but compiler is not forced to obey this hint. The main use of inline specifier is to avoid violations of One Definition Rule.

Once you declare a function inline, it needs to be defined in every translation unit it used and the definition must be exactly the same every time. It is the other way around than as your title suggests - the choice of where you define the function mandates whether it needs to be marked inline or not.

1) and 2) are okay. In the first case it is implicitly inline and in the second you explicitly declared it so. The definition is the same wherever you include the header.

Case 3) will only work if you compile and link X_impl.h as a source file. In that case there will be only one definition and inline would be redundant. This way the compiler doesn't see the definition in other translation units, though, and that makes it impossible for it to inline the function, regardless whether it is inline or not.

If the intent of X_impl.h was to reduce the visual size of a header, then you should do it the other way around, include it at the end of X.h. inline must stay in that case.

Community
  • 1
  • 1
jrok
  • 54,456
  • 9
  • 109
  • 141
  • +1. But `X-inl.h`, done as the OP did, can also serve as a dependency reducer. If you always include `.h` files in `.h` files, and `-inl.h` files in `-inl.h` files and `.cpp` files, you reduce compilation dependencies (or even break cycles), while still keeping `inline` functions defined everywhere they're needed. I use a very similar system in some of my projects. – Angew is no longer proud of SO Aug 13 '13 at 07:58
  • This is probably the best explanation so far. I meant option 3 to be like you described, reducing the size of the header. I understood what inline does, but when inline should not be used for functions that make sense to be inlined, but instead to avoid violations of the one definition rule, I don't understand its meaning. When should one need to violate the one definition rule? – Timo Türschmann Aug 14 '13 at 05:44
  • @Azzu Never, of course. But it's easy to do it accidentaly: if you define a function in a header and include it in more than one translation unit it is defined in multiple places. If you forget to mark it inline, it is a violation of ODR. The situation is similar with global variables. – jrok Aug 14 '13 at 07:35
1

Those three options are all corrects.

After it depends on such thing :

  • If your function is quite short (like getters/setters), it is more common to see the function directly defined in the class definition.

  • If your function is bigger, it can be good to define it in another header and include only this header in the source where the function is used. This will only speed up your compilation. But It is rare to inline big function.

But don't forget that, it is not because you used the inline keyword that your compile will inline your function. It is up to the compiler to decide if it will the function in every place it use or not.

This is stated explicitly in the standard :

7.1.2/2 Function specifiers [dcl.fct.spec]

A function declaration with an inline specifier declares an inline function. The inline specifier indicates to the implementation that inline substitution of the function body at the point of call is to be preferred to the usual function call mechanism. An implementation is not required to perform this inline substitution at the point of call; however, even if this inline substitution is omitted, the other rules for inline functions defined by 7.1.2 shall still be respected.

Last thing :

Most compilers already optimize code to generate inline functions when it is more convenient. This specifier only indicates the compiler that inline is preferred for this function.


As jrok stated, inline is mainly used to avoid violation of the One Definition Rule. Here we can also quote a little part of the standard :

(§ 7.1.2/4) An inline function shall be defined in every translation unit in which it is odr-used and shall have exactly the same definition in every case (3.2).

3.2 One definition rule [basic.def.odr]

No translation unit shall contain more than one definition of any variable, function, class type, enumeration type, or template.

Community
  • 1
  • 1
Pierre Fourgeaud
  • 14,290
  • 1
  • 38
  • 62
1

I would select each method favouring the readability and this depends on the size of the function:

  • one line functions --> option 1,
  • small size functions --> option 2,
  • middle size functions --> option 3,
  • big size function --> are you sure you want inlining?

If you have a great number of small size functions go for option 3 and never mix option 2 and 3 together.

Moreover, as you presented your third option, you will have to remember to include X-inl.h instead of X.h. If you modify as follows:

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;
}

Then you can include X.h as you would normally do.

DarioP
  • 5,377
  • 1
  • 33
  • 52
  • If the function is too big, it won't be inlined. It's up to the compiler in any case. – jrok Aug 13 '13 at 07:28
  • Yes, that's why you should think twice before adding constructs that won't have any effect. – DarioP Aug 13 '13 at 07:43
0

This depends on where you use your inline function, and how frequently you use it. IF the code of the inline function is short (like for most getters/setters) and if it is (likely) used in many places, putting it straight into the class definition is the straightforward way to go.

If your inline function is "massive" and used only by a few users of your class, putting it into a seperate header is best. That speeds up the compile in those cases, that the inline is not required, but requires you to communicate that extra header file to the users of your lib.

Kai Petzke
  • 2,150
  • 21
  • 29
0

I assume there may be a little confusion over the inline keyword and an inline function/method.

The keyword inline tells the compiler that it should copy the function/method code to the place where the function/method is called. Example:

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

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

shall become

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

when compiled. But the compiler is not forced to inline the function/method.

On the other hand, an inline method is implemented at the place you declare it. Example:

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

public:
    /* [...] */
    double avgPrice() {
        if (unitsSold == 0) {
            return 0.0;
        }
        return revenue/unitsSold;
    }
};
Abrixas2
  • 3,207
  • 1
  • 20
  • 22
-1

As long as you add the inline suggestion to the compiler, the three options you provide will (must) lead to the same compiled code.

That is always considering an standard compiler doing the standard compilation task...

felixgaal
  • 2,403
  • 15
  • 24
  • @JOhn, it's as clear as it can be, considering the compiler will do ehatever it considers he must do. The `inline` directive is only a **suggestion** and the compiler _can_ dismiss that suggestion if the optimizer considers to make it so. There is no clear answer to the question as it is formulated. – felixgaal Aug 13 '13 at 07:07
  • I have not down voted your answer, It is better if you improve your answer just like you written in comment. – EmptyData Aug 13 '13 at 07:32