14

I have 2 files A.cpp and B.cpp which look something like

A.cpp
----------
class w
{
public:
    w();
};


B.cpp
-----------
class w
{
public:
    w();
};

Now I read somewhere (https://en.cppreference.com/w/cpp/language/static) that classes have external linkage. So while building I was expecting a multiple definition error but on the contrary it worked like charm. However when I defined class w in A.cpp, I got the redefinition error which makes me believe that classes have internal linkage.

Am I missing something here?

Lewis Kelsey
  • 4,129
  • 1
  • 32
  • 42
Vivek
  • 143
  • 1
  • 4
  • How are you building the code ? – Arunmu Jun 24 '11 at 08:41
  • 1
    you might find [this](http://stackoverflow.com/questions/1358400/what-is-external-linkage-and-internal-linkage-in-c) useful – StevieG Jun 24 '11 at 08:46
  • 3
    The compiler won't complain if it sees the same class twice, but with exact same definition. This case won't count as *redefinition*. – Tugrul Ates Jun 24 '11 at 08:46
  • @junjames: Not true. The One Definition Rule doesn't care about content. If a name is defined twice the rule is violated, even if the new definition is exactly the same as the old one. – David Hammen Jun 24 '11 at 10:13
  • @DavidHammen That's wrong. See the citations in Praxeolitic's answer (and later added, IMO too far at the bottom, to Alok's) – underscore_d Mar 12 '17 at 17:26

6 Answers6

24

The correct answer is yes, the name of a class may have external linkage. The previous answers are wrong and misleading. The code you show is legal and common.

The name of a class in C++03 can either have external linkage or no linkage. In C++11 the name of a class may additionally have internal linkage.

C++03

§3.5 [basic.link]

A name is said to have linkage when it might denote the same object, reference, function, type, template, namespace or value as a name introduced by a declaration in another scope

Class names can have external linkage.

A name having namespace scope has external linkage if it is the name of

[...]

— a named class (clause 9), or an unnamed class defined in a typedef declaration in which the class has the typedef name for linkage purposes (7.1.3)

Class names can have no linkage.

Names not covered by these rules have no linkage. Moreover, except as noted, a name declared in a local scope (3.3.2) has no linkage. A name with no linkage (notably, the name of a class or enumeration declared in a local scope (3.3.2)) shall not be used to declare an entity with linkage.

In C++11 the first quote changes and class names at namespace scope may now have external or internal linkage.

An unnamed namespace or a namespace declared directly or indirectly within an unnamed namespace has internal linkage. All other namespaces have external linkage. A name having namespace scope that has not been given internal linkage above [class names were not] has the same linkage as the enclosing namespace if it is the name of

[...]

— a named class (Clause 9), or an unnamed class defined in a typedef declaration in which the class has the typedef name for linkage purposes (7.1.3);

The second quote also changes but the conclusion is the same, class names may have no linkage.

Names not covered by these rules have no linkage. Moreover, except as noted, a name declared at block scope (3.3.3) has no linkage. A type is said to have linkage if and only if:

— it is a class or enumeration type that is named (or has a name for linkage purposes (7.1.3)) and the name has linkage; or

— it is an unnamed class or enumeration member of a class with linkage;

Some of the answers here conflate the abstract notion of linkage in the C++ Standard with the computer program known as a linker. The C++ Standard does not give special meaning to the word symbol. A symbol is what a linker resolves when combining object files into an executable. Formally, this is irrelevant to the notion of linkage in the C++ Standard. The document only ever addresses linkers in a footnote regarding character encoding.

Finally, your example is legal C++ and is not an ODR violation. Consider the following.

C.h
----------
class w
{
public:
    w();
};


A.cpp
-----------
#include "C.h"


B.cpp
-----------
#include "C.h"

Perhaps this looks familiar. After preprocessor directives are evaluated we are left with the original example. The Wikipedia link provided by Alok Save even states this as an exception.

Some things, like types, templates, and extern inline functions, can be defined in more than one translation unit. For a given entity, each definition must be the same.

The ODR rule takes content into consideration. What you show is in fact required in order for a translation unit to use a class as a complete type.

§3.5 [basic.def.odr]

Exactly one definition of a class is required in a translation unit if the class is used in a way that requires the class type to be complete.

edit - The second half of James Kanze's answer got this right.

Praxeolitic
  • 22,455
  • 16
  • 75
  • 126
8

Technically, as Maxim points out, linkage applies to symbols, not to the entities they denote. But the linkage of a symbol is partially determined by what it denotes: symbols which name classes defined at namespace scope have external linkage, and w denotes the same entity in both A.cpp and B.cpp.

C++ has two different sets of rules concerning the definition of entities: some entities, like functions or variables, may only be defined once in the entire program. Defining them more than once will result in undefined behavior; most implementations will (most of the time, anyway) give a multiple definition error, but this is not required or guaranteed. Other entities, such as classes or templates, are required to be defined in each translation unit which uses them, with the further requirement that every definition be identical: same sequence of tokens, and all symbols binding to the same entity, with a very limited exception for symbols in constant expressions, provided the address is never taken. Violating these requirements is also undefined behavior, but in this case, most systems will not even warn.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • Thanks James. I believe it answers my question that even on modifying the definition of class w in one of the .cpp files (just introduced one variable) so that the sequence of tokens are different in both the definitions, program compiled error free. – Vivek Jun 24 '11 at 09:38
  • Yes. It's undefined behavior; most implementations will not detect the error. Depending on what the differences are, and how you use the classes, however, you can get unexpected or unpleasant behavior at runtime. – James Kanze Jun 24 '11 at 10:09
4

The class declaration

class w
{
public:
    w();
};

does not produce any code or symbols, so there is nothing that could be linked and have "linkage". However, when your constructor w() is defined ...

w::w()
{
  // object initialization goes here
}

it will have external linkage. If you define it in both A.cpp and B.cpp, there will be a name collision; what happens then depends on your linker. MSVC linkers e.g. will terminate with an error LNK2005 "function already defined" and/or LNK1169 "one or more multiply defined symbols found". The GNU g++ linker will behave similar. (For duplicate template methods, they will instead eliminate all but one instance; GCC docs call this the "Borland model").

There are four ways to resolve this problem:

  1. If both classes are identical, put the definitions only into one .cpp file.
  2. If you need two different, externally linked implementations of class w, put them into different namespaces.
  3. Avoid external linkage by putting the definitions into an anonymous namespace.
namespace
{
  w::w()
  {
    // object initialization goes here
  }
}

Everying in an anonymous namespace has internal linkage, so you may also use it as a replacement for static declarations (which are not possible for class methods).

  1. Avoid creating symbols by defining the methods inline:
inline w::w()
{
  // object initialization goes here
}

No 4 will only work if your class has no static fields (class variables), and it will duplicate the code of the inline methods for each function call.

following
  • 137
  • 2
  • 7
  • 2
    This seems to be wrong a lot of points. (2) well, sure, but then they won't be the same `class w`. (3a) but if you had tried this, you'd realise it doesn't work, as the class was not/must also be declared in the same anonymous/unnamed namespace - which by definition would give it internal linkage, thus defeating the point... (3b) what? `static` class methods are allowed. (4a) I don't think `inline` does what you think it does; specifically, definitions must still be identical, & `inline` is defined as conferring symbols with external linkage... (4b) LTO can probably remove the code duplication – underscore_d Mar 12 '17 at 17:32
  • So there is no other way... such as... defining a classes... then exporting it somehow? – Aftershock Jan 13 '19 at 10:45
  • Linking != linkage – Tim Randall Mar 30 '21 at 20:07
0

External linkage means the symbol (function or global variable) is accessible throughout your program and Internal linkage means that it's only accessible in one translation unit. you explicitly control the linkage of a symbol by using the extern and static keywords and the default linkage is extern for non-const symbols and static (internal) for const symbols.

A name with external linkage denotes an entity that can be referenced via names declared in the same scope or in other scopes of the same translation unit (just as with internal linkage), or additionally in other translation units.

The program actually violates the One Definition Rule but it is hard for the compiler to detect the error, because they are in different compilation units. And even the linker seems cannot detect it as an error.

C++ allows a workaround to bypass the One Definition Rule by making use of namespace.

[UPDATE] From C++03 Standard
§ 3.2 One definition rule, section 5 states:

There can be more than one definition of a class type ... in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. Given such an entity named D defined in more than one translation unit, then each definition of D shall consist of the same sequence of tokens.

Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • Thanks Als. It means that the ODR applies to classes only when there are multiple definitions in the same translational unit. But it makes me wonder why would we have this kind of special treatment for classes. I mean we can always choose to have multiple definitions of classes/symbols by using the concept of namespaces but in this case we are bypassing the ODR without using namespaces. – Vivek Jun 24 '11 at 09:14
  • One more thing regarding the statement "..Given such an entity named D defined in more than one translation unit, then each definition of D shall consist of the same sequence of tokens.." I modified the definition of class w in one of the .cpp files (just introduced one variable) so that the sequence of tokens are different in both the definitions, but still I was able to compile the program error free. – Vivek Jun 24 '11 at 09:29
  • The compiler cannot detect the violation because, as you note, the violation occurs in separate compilation units. The linker cannot detect the error because the only symbols defined are A::w() (plus the freebie default constructor, copy constructor, and destructor for class A), and they are all inlined. The linker will complain if the definitions of A::w() are moved outside the class and are not qualified with inline. – David Hammen Jun 24 '11 at 10:19
  • @Vivek: As I mentioned, it is very hard for the compiler to detect the error because they are in different compilation units. Seems like one of those dark murky boundary line cases where the compiler is just not good enough to detect. – Alok Save Jun 24 '11 at 10:44
  • 2
    This answer is wrong. @Vivek Each translation unit must contain the definition of every class used as a complete type so that they can known its size in memory. Classes not used as complete types, e.g. one for which a translation unit declares a pointer to an instance, need not be defined because the translation unit does not need to know the size in memory. – Praxeolitic Sep 26 '14 at 04:39
0

Classes have no linkage to be pedantic.

Linkage only applies to symbols, that is, functions and variables, or code and data.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
0

Seeing as you can't use static on a class, the only way to give a 'class' static linkage is to define the type in an anonymous namespace. Otherwise, it will have extern linkage. I put class in quotation marks because a class, which is a type, does not have a linkage, instead it is referring to the linkage of the symbols defined in the class scope (but not the linkage of an object made using the class). This includes static members and methods and non-static methods, but not non-static members as they are only part of the class type definition and do not additionally declare / define actual symbols.

The 'class' having static linkage means that the members and methods that would have had external linkage or external comdat linkage now both have static linkage only -- they are now local symbols, although the effect of inline at the compiler level still applies (i.e. it does not emit a symbol if it is not referenced in the translation unit) -- it's just no longer an external comdat symbol at assembler level, it's a local symbol. This is the case even if the member or method of the class is defined out-of-line and out of an anonymous namespace, it will still have static linkage.

If you declare the class type in an anonymous namespace, you will not be able to define the type outside of an anonymous namespace and it will not compile. You need to define it in the same anonymous namespace or a different anonymous namespace in the translation unit (different anonymous namespace doesn't matter because they're all combined into the same anonymous anonymous namespace name _GLOBAL__N_1).

This is the only way to change the linkage of members or methods of a class / struct because static will make it a static member and does not change the linkage, static will be ignored on out of line definitions, and extern is not allowed on class members / functions.

Lewis Kelsey
  • 4,129
  • 1
  • 32
  • 42