10

I'm a beginner in C++, and I was wondering if it is best practice to define a member function directly in a class, such as:

// something.hpp
class C {
    inline int func() { return ... ; }
}

rather than the usual:

// something.hpp
class C {
    int func();
}
// something.cpp
int C::func() { return ... ; }

In my reading of Stroustrup's "The C++ Programming Language", I have seen him use either technique. Alas, I am confused as to whether it is good to do so in real-world applications and, in particular, the creation of libraries.

6
  • 1
    What's your research on C and C++ compilation times? Same question for the inline keyword. Apr 30 at 2:15
  • 1
    By reading tutorials online and on StackOverflow, I have learned that "inline" simply tells the compiler to supplant function calls with their definition when possible. This is ideal for short functions, mainly those defined within classes. I believe any function defined within its class defaults to inline, however, so I think I would be fine leaving it out as well.
    – Angel E.
    Apr 30 at 2:51
  • 4
    That definition of inline is sadly out of date. If a function is practically defined in-line (ie, in the header), modern compilers are perfectly capable of flattening out calls to it without the inline keyword. It is these days essentially a type of linkage providing an exemption from the One Definition Rule.
    – Useless
    Apr 30 at 12:18
  • 2
    I dont know if my usage is out of date, but one of the reasons I might move a method into a cpp file is because of circular dependencies. If I need to use another class in the method, and that other class needs to use this class in its methods... well let's just say that causes me to not be able to define both methods in the header files.
    – Omroth
    Apr 30 at 17:30
  • That is a very old book, and the language has changed substantially as of C++11, and best practices have emerged long after that was written.
    – JDługosz
    Apr 30 at 21:20
18

When a function is defined as part of the class definition, it is implicitly inline (regardless of whether you use that keyword). This has a number of consequences.

  • The function may be inlined by the compiler, potentially but not necessarily resulting in a faster program.
  • Thus, different compilation units can get different copies of this function.
  • The One Definition Rule (ODR) does not apply, which usually requires that each function or object has only one definition in the entire program. Instead, you promise that all copies of the function are equivalent. (This could be violated if you change the function in the header, but don't recompile all compilation units that include the header).
  • Changing an inline function breaks ABI compatibility, requiring recompilation.

Whether the function is defined as part of the class definition, or as an inline function outside of the class definition but still within the header is equivalent:

class C {
    int func();
}

inline int C::func() { return ... ; }

Now when we put the function definition into a separate compilation unit, we have a different set of consequences:

  • The function cannot be inlined (unless your compiler does link-time optimization) which might be slightly less efficient.
  • The function is only defined in that compilation unit. To call the function from other compilation units, the object code has to be linked by the compiler.

The large practical difference is what happens when you modify this function.

  • For an inline function, you have to recompile all code that uses this definition.
  • For a non-inline function, you only have to compile that one compilation unit that defines it, and then re-link with other compilation units.

On large projects, avoiding recompilation is very important in order to enable fast feedback when making changes. This leads to various strategies:

  • keep header files as minimal as possible, possibly have multiple header files so that dependent compilation units can only pull in those declarations they need
  • prefer defining functions in separate compilation units
  • if compile time performance and ABI stability are more important than run time performance, the pImpl idiom can be used

However, inline functions still have their uses:

  • the function definition is trivial, unlikely to change, and should be inlined, e.g. getters or setters
  • templates are inline
  • the function should be available for inlining due to performance reasons

If you want to enable inlining optimizations within a compilation unit, declaring it as inline is not necessary or appropriate. It is more important that the function has “internal linkage”. For example, free functions (that are not class members) have internal linkage when declared static. The contents of an anonymous namespace have internal linkage. This is useful for helpers within a .cpp file.

If performance and compilation times and ABI stability are no concern, then defining your functions as part of the class definition is perfectly fine. This is the case in many smaller projects that are internally used libraries or executables. Some people prefer having the function definitions right in the class. Other people prefer keeping the definitions separate, so that it's easier to get an overview of all members of the class.

This answer applies to all versions of the C++ standard, with the caveat that the C++ 20 module system changes this. If a function is defined within a class definition that is part of a module, it will not be considered inline.

1
  • 1. Inline-definition makes it more available for non-lto inlining. Inlining being a possible consequence makes it seem impossible without. Which you later take back. 2. Different TUs possibly having different copies is nothing new. Even a single TU might have multiple copies due to optimization. Still only one representative candidate, arbitrarily chosen, will get its address taken. Unless we come to dll boundaries and all the fun they are. Apr 30 at 18:14
5

This isn't really an issue of safety. It's mostly about readability and a little bit about compile times.

If a function is very long, you'd generally prefer not to have the entire implementation in the header. Ideally the header should simply declare an interface, and leave all the implementation details hidden from the outside world.

That said, when you're dealing with a really tiny function that (for example) just returns the value of some member variable, just putting the implementation right in the header is pretty harmless even at worst.

My personal rule of thumb is that if it's more than about two or three lines long, it should probably go in the implementation file, instead of living directly in the header (but it's ultimately a judgement call).

2
  • Ah, okay. I was just confused since I had not seen a non-trivial library header with member functions defined this way. If it's "harmless even at worst", then I will continue to do this in the implementation of classes. Because "the header should simply declare an interface", however, I will leave it out of the representation.
    – Angel E.
    Apr 30 at 2:46
  • 1
    Also, writing the implementation in an implementation file speeds up compilation time. If you are writing large a library, and you make a change to a header file, then every implementation file including that header must be re-compiled. If, on the other hand, the implementation code is in an implementation file, then only the implementation file needs re-compilation when its code is updated. Actually, this is the reason for the PIMPL idiom. Apr 30 at 17:26
0

Note that having the function body inside the class definition and having the body present in the header are different things. You can also write:

// something.hpp
class C {
    int func();
};  // semicolon missing in your original!

// note:  still in something.hpp
inline int C::func() { return ... ; }

And this gives you the ability for client code to easily inline the function (without LTO), allows you to ship header-only libraries, and keeps the class definition a clean synopsis.

Put the body directly in the class definition if it is sufficiently simple, so as not to clutter the reading of the class or add a bunch of lines between the members.

Whether to put the body in the header is a separate decision. It might be too large, but you still want it to be readily inlined, or is a template.

Sometimes, you can't put the body in the class because it needs to know about other classes that are defined later. You have to put the body after all the needed classes are defined.

Sometimes, more rarely now, the compiler requires something to be directly in the class or it just doesn't work.

4
  • There is a variant: The header-implementation-file, often type .tpp or such. Included at the end of the true header-file only to provide inline definitions, mostly templates. Modules will likely over time cause their extinction. Apr 30 at 21:45
  • 1
    As far as the compiler is concerned, that is the same as "the header".
    – JDługosz
    Apr 30 at 21:54
  • Some readers won't conceive of that idea at all, especially if they never saw it. There are TUs, and whether some file is a "header", "footer" or whatever does not concern the compiler. You know, there are some projects (especially before lto) which are (or can be optionally) compiled as a single TU. Does that make all the files "headers" but the master-includer? Apr 30 at 22:12
  • It's a "header" if the contents is, or could be, included in more than one translation unit.
    – JDługosz
    May 3 at 14:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.