2

Consider the following four member function declarations and definitions:

// ==== file: x.h
#ifndef X_H
#define X_H
class X {
 public:
  int a(int i) { return 2 * i; }
  inline int b(int i) { return 2 * i; }
  int c(int i);
  int d(int i);
};

inline int X::c(int i) { return 2 * i; }
int X::d(int i) { return 2 * i; }
#endif

For completeness, here's the .cpp file that instantiates an X and calls the methods...

// ==== file: x.cpp
#include "x.h"
#include <stdio.h>

int main() {
  X x;
  printf("a(3) = %d\n", x.a(3));
  printf("b(3) = %d\n", x.b(3));
  printf("c(3) = %d\n", x.c(3));
  printf("d(3) = %d\n", x.d(3));

  return 0;
}

My question: are there any salient differences among the four methods? I understand from a comment in this post that the compiler may automatically inline methods that are defined in the class definition.

update

Many answers assume that I'm asking about the difference between inlining and not. I'm not. As I mentioned in the original post, I understand that defining a method in the header file gives the compiler license to inline the method.

I also (now) understand that method d is is risky as written: since it is not inlined, it will be multiply defined if there are multiple translation units.

My question remains: are there any salient differences among the four methods? (As noted, I know that method d is different). But -- just as important -- are there stylistic or idiomatic considerations that would make a developer choose one over the others?

Community
  • 1
  • 1
fearless_fool
  • 33,645
  • 23
  • 135
  • 217
  • The definition of `X::d` violates One definition rule if the header is included in multiple translation units. Generaly, functions defined in headers need `inline` or `static` specifier. Conversely, `inline` in `X::b` definition is implicit. – jrok Apr 28 '14 at 17:33
  • 1
    `a`, `b`, and `c` are all equivalent. The definition of `d` may only appear in one translation unit. – aschepler Apr 28 '14 at 17:53
  • @aschepler: Your comment appears to be the first proper response I've seen. Make it an answer so I can give it a checkmark. – fearless_fool Apr 28 '14 at 17:56
  • I agree. I gave a good answer to the wrong question; @aschepler has it right. user3521733's caveat is important too. – dlf Apr 28 '14 at 18:11
  • 1
    You might be misunderstanding what it means for a functon "to be inline" – John Dibling Apr 28 '14 at 18:39
  • When you ask for opinions on stylistic considerations, you risk starting a holy war, but in this case, I think I speak for most sane programmers when I say that function defs inside class defs are usually something to be avoided, except perhaps for getters and setters. You want the header file to be as clean as possible because it tells someone who hasn't used the class before how to use it. – bcrist Apr 28 '14 at 20:14
  • @bcrist The OP is not comparing function definitions in header vs definitions in source file, he's comparing function definitions inside vs outside the class definition, where **both** cases are all within the header file. – JBentley Apr 28 '14 at 20:16
  • @JBentley I didn't say anything about source files. Putting member function defs in the class def is less clear than putting them at the end of the `.h`, or moving them to a `.inl` file that's included by the header. – bcrist Apr 28 '14 at 20:24

5 Answers5

5

Since this answer keeps getting upvotes, I feel obligated to improve it. But much of what I'm adding has already been stated in other answers and comments, and those authors deserve the credit.

On the subject of whether there's a difference between placing a function body inside the class definition or just below it (but still in the header file), there are 3 different cases to think about:

1) The function is not a template and is not declared to be inline. In this case it must be defined in the class definition or a separate cpp or you will get a linker error as soon as you try to include the h in more than one compilation unit.

2) The function is a template, but is not declared inline. In this case, putting the body within the class definition provides a hint to the compiler that the function can be inlined (but the final decision is still at its own discretion).

3) The function is declared to be inline. In this case there is no semantic difference, but it may sometimes be necessary to place the function body at the bottom in order to accommodate dependency cycles.

Original answer, which provides good info but does not address the actual question:

You've already noted the inline difference. In addition, defining member functions in the header means your implementation is visible to everyone. More importantly, it means everyone who includes your header also needs to include everything needed to make your implementations work.

dlf
  • 9,045
  • 4
  • 32
  • 58
  • @dif: Noted, but I wasn't asking about the difference of putting the methods in the .h file vs the .cpp file (though perhaps I should have made that clearer). I was asking about putting the method definition inside or outside of the class definition, all within the .h file. – fearless_fool Apr 28 '14 at 17:50
  • Ah; my mistake. In that case, aside from potential inlining the main consideration is readability and whether you have ordering constraints placed on you like user3521733 describes. – dlf Apr 28 '14 at 17:52
  • Further, as @MichaelJ notes, the definition of D will cause a linker error if you include x.h in more than one module. C is ok since it is declared to be inline. – dlf Apr 28 '14 at 17:57
2

If you are going to inline it regardless, then you'd move it out of the class if you want to be able to see all your members in one screen, or you have a cyclic dependency as mentioned below. If you don't want to inline it, then you have to move it out of the class and into an implementation file.

In the cases of classes that cyclically refer to each other, it may be impossible to define the functions in the classes so as to inline them. In that case, to achieve the same effect, you need to move the functions out of the classes.

Doesn't compile:

struct B;
struct A {
    int i;
    void foo(const B &b) {
        i = b.i;
    }
};

struct B {
    int i;
    void foo(const A &a) {
        i = a.i;
    }
};

Does compile, and achieves the same effect:

struct B;
struct A {
    int i;
    inline void foo(const B &b);
};

struct B {
    int i;
    inline void foo(const A &a);
};

inline void A::foo(const B &b) {
    i = b.i;
}
inline void B::foo(const A &a) {
    i = a.i;
}
kec
  • 2,099
  • 11
  • 17
1

Oops, just realised you had the definitions in the header file. That creates problems if the include file is included in more than one place.

If the functions are defined in a CPP file then there is no difference.

Michael J
  • 7,631
  • 2
  • 24
  • 30
  • No worries. The #ifdef is supposed to sidestep the issue of the header being included more than one place. To your last statement, if I defined the methods in the cpp file then I'd lose the benefit of inlining. – fearless_fool Apr 28 '14 at 17:59
  • @fearless_fool Include guards do nothing to prevent multiple inclusions in different translation units. – jrok Apr 28 '14 at 18:03
  • @jrok - True that. I now understand why `X::d` is problematic. Thanks. – fearless_fool Apr 28 '14 at 18:06
  • I disagree with *"if the functions are defined in a CPP file then there is no difference".* There are plenty of differences, several of which have been mentioned in other answers. Perhaps, "no semantic difference" would be more accurate. – JBentley Apr 28 '14 at 20:13
0

The only time it makes sense to implement a function inline is when the function is very clearly trivial and/or it has performance implications.

For all other times, it's best to put them in a .cc file and keep its implementation not exposed to the user of the class.

As pointed out by user3521733, it is impossible to implement some functions in the header file when there are cyclic dependencies. Here you are forced to put the implementations in a .cc file.

Update

As far as the compiler, and the runtime, is concerned, there is no difference that I can think of between defining the function inside the body of the class or outside if you use inline when defining it outside the body of the class.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • I wasn't asking about inlining vs not inlining specifically (besides, a good compiler should figure that out). I was asking about the difference between defining the method body inside vs outside the class definition (i.e. method b vs method c). – fearless_fool Apr 28 '14 at 17:53
  • It's not impossible to implement anything in the header file. Just mark the definition `inline` (like @user3521733 did) if it's in a header. – aschepler Apr 28 '14 at 17:55
0

X::a, X::b and X::c are all inlined. X::d is not. That's the only real differnce between these functions, aside from the fact that they are all different functions. The fact that X::c is defined in the header is irrelevant. What is relevant there is that the definition is marked inline.

In order to understand what the differences are, it's important to understand what inline is and is not. inline is not a performance tweak. It's not about making your code faster, and it's not about blowing the code out inline.

What it is about is the ODR. A function marked inline will have the exact same definition in each translation unit where it is used.

This comes in to play when you try to #include your file above in two or more CPP files and call X::d in those translation units. The linker will complain that X::d is defined more than once -- you've violated the ODR. The fix to this is to either mark the function inline or move the definition to it's own translation unit. (eg, to a CPP file)

Community
  • 1
  • 1
John Dibling
  • 99,718
  • 31
  • 186
  • 324
  • Best answer yet: `a`, `b` and `c` are identical. And I get the significance of ODR. Are there stylistic reasons to prefer one over the other two? I've seen all three flavors used. – fearless_fool Apr 28 '14 at 20:05
  • @fearless_fool: I think there are no universal guidelines as to which to prefer, aside from the fact that `X::d` must be marked `inline` explicitly. Personally I prefer to keep implementations in the CPP files as much as is reasonable, because it makes maintainence easier for me. – John Dibling Apr 29 '14 at 16:44