0

I simplified the code to 2 files compiled independently and linked together.

file: main.cpp

class A{
    public:
        int value;
        A(int);
};

#include <iostream>
using namespace std;

int main(int argc, char **argv)
{ A a{7};
    cout<<"a.value="<<a.value<<endl;
    return 0;
}

file: classA.cpp

class A{
    public: 
        int value;
        A(int x):value{x}{};
};

//A::A(int x):value{x}{}

From my understanding of the process, when classA.cpp get's compiled, a function "A::A" is created (a constructor of the Class). If I comment the line A(int x):value{x}{} and uncomment the commented one the result into the object file should be the same.

But in the first case the two object files do not link together, and in the other case they do link. Why is that ?

I checked the object file generated for classA and in the first case is empty, so it is normal that it won't link, But why ?

George Kourtis
  • 2,381
  • 3
  • 18
  • 28
  • 2
    You have two definitions for class A. That isn't going to work. – Retired Ninja Aug 20 '23 at 19:42
  • You also have quite a few other questions that have gotten answers that you haven't followed up on. – Ted Lyngmo Aug 20 '23 at 19:45
  • These two definitions are in different compilations. Once the object files are created during two different compilations, where only one file is compiled at a time, there are produced two different object files. If these have the correct "mangled_names" then the name referred from the one will be in the other, so the linkage will succeed. If not it will not. – George Kourtis Aug 20 '23 at 20:06
  • 2
    This just looks like a straightforward violation of the [one-definition rule](https://en.cppreference.com/w/cpp/language/definition) and all the talk about mangling and the implementation detail of what exactly makes it into the object file is basically irrelevant. Key quote: "There can be more than one definition in a program of each of the following: class type ... as long as all of the following is true: ... each definition consists of the same sequence of tokens (typically, appears in the same header file)". Your two definitions of A violate this rule. – Nathan Pierson Aug 20 '23 at 21:28
  • @NathanPierson I finally understood the problem, see my answer at the end if you like. Answering to your point: the definition in main.cpp of the class is the same as in classA.cpp, with the only difference that in classA.cpp the method is implemented. The fact is that whatever you write in a compilation unit inside the files of that compilation unit REMAINS SECRET to that compilation. The only way to "share" the definition is to share the class.cpp as a "header" file ( even if it contains code ). See my detailed answer bellow! – George Kourtis Aug 20 '23 at 21:41

3 Answers3

3

The definitions of the two As are different. One has an inline definition of the constructor and the other has not. This is not allowed and would cause undefined behavior if linking had succeeded.

If you make the definitions exactly the same and then define the constructor non-inline:

A::A(int x):value{x}{}

Then the linkage will succeed and the program will have defined behavior.

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • changing to "no inline" should suffice ? – George Kourtis Aug 20 '23 at 20:18
  • @GeorgeKourtis Yes, if that makes the class definitions the same. See it as having a header file but here you instead copy the class definition into two TUs. You shouldn't change any of them (in any way that matters - whitespaces and comments may be different for example). – Ted Lyngmo Aug 20 '23 at 20:19
  • to me semantically they should be the same, the inlining may be a problem that a separate compilation is unable to detect. But also putting __attribute__ ((noinline)) it does not seem to work, so there is something in the semantics that should be paid attention to in the documentation of classes, and I don't understand it for now ! – George Kourtis Aug 20 '23 at 20:23
  • Ok, but an inline function is not the same as a non-inline function so, no, they are not the same. – Ted Lyngmo Aug 20 '23 at 20:24
  • You mean attributes take part in name mangling ? – George Kourtis Aug 20 '23 at 20:25
  • @GeorgeKourtis Attributes _should_ afaik be apart of name mangning but I know that there are problems with some of them in gcc and clang, so perhaps that may slip through in some cases. – Ted Lyngmo Aug 20 '23 at 20:27
  • By defining the function in classA outside the class definition as: inline A::A(int x):value{x}{} the effect is the same as before and it is not possible to link. So to clarify what you already pointed out: functions that are inlined DO NOT APPEAR in the object FILE. – George Kourtis Aug 20 '23 at 20:46
0

The code as you show in classA.cpp is a declaration for class A with inline definition of the constructor. When you link classA.o and main.o together, you have repetitive declarations of class A.

A::A(int _a){...} on the other hand is just a definition of the constructor that complements the class declaration in main.cpp

fjs
  • 330
  • 2
  • 9
  • I don't think it is exactly like that. Compilation is done for main.cpp, and for classA.cpp. These are two different compilations and do not depend one on the other. – George Kourtis Aug 20 '23 at 20:04
  • @GeorgeKourtis Yeah, the compilation of two cpp files are independent, but when linking them to the executable, the linker will look for definition of class A's ctor, and there should only be one definition of that constructor A(int). – fjs Aug 20 '23 at 20:09
  • From my understanding of the process of compilation and linking, once the object file is generated, there is nothing related to anything that has to do with the specific language that produced the object file. So e.g. we may link two files compiled with different programming languages. So the only think that matters is what is in the object file. What I already stated is that in the first case the object file does not contain anything !!! and this is the reason that linking cannot proceed. The question is WHY ? – George Kourtis Aug 20 '23 at 20:12
  • To better answer your point, compilation of main.cpp does generate not generate an entry related to A::A(int) instead should generate a reference. Instead compilation of classA should generate code and an entry point with the correctly mangled A::A(int) – George Kourtis Aug 20 '23 at 20:16
-2

As far as I have understood things until now, methods defined inside the class definition, are never exposed in the object file (thus you will not find their name doing an objdump -d on the object file). This is true, either if these are inlined or not. The idea is that what is inside the class definition or "header", is included in every file that makes reference to that "header". Pay attention that in our case such a "header" does contain code too. Given that the above code is included in every file that makes use of the "header" of the class, these functions are recompiled and code is regenerated in every object file that uses the header, so the same function name does not clash with the function names in other object files because none of these definitions has external exposure.

I read from the standard (found the reference from https://softwareengineering.stackexchange.com/questions/56215/why-can-you-have-the-method-definition-inside-the-header-file-in-c-when-in-c-y ) that:

*9.2.1 Member functions

1 A member function may be defined (8.4) in its class definition, in which case it is an inline member function (7.1.2) etc.... (note inline does not mean that is really inlined !!! )

2 An inline member function (whether static or non-static) may also be defined outside of its class definition provided either its declaration in the class definition or its definition outside of the class definition declares the function as inline or constexpr. [Note: Member functions of a class in namespace scope have the linkage of that class. Member functions of a local class (9.4) have no linkage. See 3.5. — end note ] 3 [ Note: There can be at most one definition of a non-inline member function in a program. There may be more than one inline member function definition in a program. See 3.2 and 7.1.2. — end note ]*

Simple ? I don't think !

George Kourtis
  • 2,381
  • 3
  • 18
  • 28
  • 1
    What's with the scare quotes around "header"? This answer doesn't make a lot of sense to me. It has some summaries of what you've observed about the implementation details of whatever compiler you use, and then some quotes from the standard that are at best tangential to the ODR violation that is your actual problem, and some editorial asides that don't really explain very much. – Nathan Pierson Aug 20 '23 at 21:47
  • @NathanPierson The quotes around "header" are because it is usually intended that a "header" does not contain code, so it does not generate things that go in the object file ( it may generate reference though to find in other object files ). In our case this kind of "header" does contain code, this code goes into the object file, but this code is not visible to other object files. So if you want that code in other object files you must reinclude it, reinterpret it, recompile it and regenerate the same code in another object file. – George Kourtis Aug 20 '23 at 21:59
  • @NathanPierson What I UNDERSTOOD IS THAT: The methods defined INSIDE A CLASS do go in the OBJECT FILE as code (only if used), but are NOT VISIBLE from other object files. In the case above no code went into the object file. – George Kourtis Aug 20 '23 at 22:10