4
votes

Let's start with the following set of C++ files:

// my_class.h
struct MyClass
{
  void my_func();
};

void MyClass::my_func()
{}


// f.h
void f1();
void f2();


// f1.cpp
#include "my_class.h"

void f1()
{
  MyClass a;
  a.my_func();
}


// f2.cpp
#include "my_class.h"

void f2()
{
  MyClass a;
  a.my_func();
}


// main.cpp
#include "f.h"

int main()
{
  f1();
  f2();

  return 0;
}

I tried to compile this code with

$ g++ f1.cpp f2.cpp main.cpp

Obviously, the linker complained of duplicate symbol my_func:

duplicate symbol __ZN7MyClass7my_funcEv in:
    /var/folders/yj/zz96q16j6vd1dq1_r3mz8hzh0000gn/T/f1-962ae7.o
    /var/folders/yj/zz96q16j6vd1dq1_r3mz8hzh0000gn/T/f2-aef78c.o
ld: 1 duplicate symbol for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

The next attempt I had was to move the function definition inside the class definition, so we get:

struct MyClass
{
  void my_func()
  {}
};

Running the same g++ command, the program compiles successfully. This is because functions defined in class definition are implicitly marked inline (ยง10.1.6 * 3).

The standard states:

A function declaration (11.3.5, 12.2.1, 14.3) 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 specified in this section shall still be respected

Which seems somewhat in contradiction to what is written on cppreference.com:

Because the meaning of the keyword inline for functions came to mean "multiple definitions are permitted" rather than "inlining is preferred", [...]

So as far as I understand, having a function defined in the class definition makes it implicitly inline which does not necessarily mean that the compiler will choose to inline its body but will a definition of it in every translation unit. Is this correct?

The second question comes regarding template classes. Separating the declaration/definition of a template class is a problem, as described here so we can only have function definitions in the class definition which makes it implicitly inline, again, right? What is the impact of this?

Since we only have the choice of defining functions in class definitions when the class is a template, what is to be done about classes that are not template? Should we define function in source files and only keep the declaration in headers when possible?

1
The problem is that in your case my_func isn't defined in the class definition. It's only declared there. - NO_NAME
@NO_NAME The second example shows exactly this: having the function definition inside the class definition - Victor
"Since we only have the choice of defining functions in class definitions when the class is a template" this is not true, so rest of the question is useless - Slava
"The second question comes" and one question per question please - Slava
@Victor Yeah, don't think too hard about include guards. They are useful, but pointless in the context of this question. - NO_NAME

1 Answers

4
votes

So as far as I understand, having a function defined in the class definition makes it implicitly inline which does not necessarily mean that the compiler will choose to inline its body but will a definition of it in every translation unit. Is this correct?

Correct. When defined inside the class it is marked as inline and it is okay that that defenition is brought into multiple translation units. The compiler will handle that for you.

The second question comes regarding template classes. Separating the declaration/definition of a template class is a problem, as described here so we can only have function definitions in the class definition which makes it implicitly inline, again, right? What is the impact of this?

This is not correct. Templates are special. They aren't actually anything that will exist once the code is compiled. What a template is, is a recipe for stamping out a class or function. As such, they are implicitly inline as well to allow the template to be included in every translation unit that uses it so the compiler can stamp out a concrete class/function from it as needed. This means you can define class member functions outside of the class.

Since we only have the choice of defining functions in class definitions when the class is a template, what is to be done about classes that are not template? Should we define function in source files and only keep the declaration in headers when possible?

Typically you want to put your definitions in a cpp file. The benefit you get from this is you only need to recompile that one cpp file if you change the implementation of the functions. If they were in the header file then you need to recompile every cpp file that includes that header file which leads to longer build times.