4

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?

Victor
  • 13,914
  • 19
  • 78
  • 147
  • The problem is that in your case `my_func` isn't defined in the class definition. It's only declared there. – Piotr Siupa Jul 25 '19 at 21:20
  • @NO_NAME The second example shows exactly this: having the function definition inside the class definition – Victor Jul 25 '19 at 21:21
  • 1
    "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 Jul 25 '19 at 21:22
  • Your header file needs header guards to prevent multiple inclusions. – Jesper Juhl Jul 25 '19 at 21:22
  • 1
    "The second question comes" and one question per question please – Slava Jul 25 '19 at 21:23
  • @JesperJuhl but even with an inclusion guard, won't it get included twice since there are 2 translation units? – Victor Jul 25 '19 at 21:23
  • @Slava show me a reasonable way of separating declaration/definition for a template class without abusing of anything at all – Victor Jul 25 '19 at 21:25
  • 1
    @Victor Yeah, don't think too hard about include guards. They are useful, but pointless in the context of this question. – Piotr Siupa Jul 25 '19 at 21:26
  • You can define a method outside of template class and mark it inline. You can decide to implement only certain specializations and put them into one cpp file. There are many variants. So having them inside class is most often used, but not only possible solution. Do you understand the difference? – Slava Jul 25 '19 at 21:27
  • @Slava I think I understand the difference, and I think I am not that stupid so let's keep things civilised. I am trying to understand a concept that's not so simple I guess. – Victor Jul 25 '19 at 21:29

1 Answers1

4

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.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • 1
    `the template needs to be included in every translation unit that uses it` - Poor choice of words. Only declaration of the template has to be included. You can put the definition in a single translation unit and instantiate it explicitly. – Piotr Siupa Jul 25 '19 at 21:30
  • @NO_NAME I've updating the wording. Better? – NathanOliver Jul 25 '19 at 21:35