I was reading some articles on cplusplus.com and for kicks I read the template tutorial. At the very end of the section there is this paragraph:

Since no code is generated until a template is instantiated when required, compilers are prepared to allow the inclusion more than once of the same template file with both declarations and definitions in a project without generating linkage errors.

Does this mean that if I have a .h file with a templated class in it I don't need to have inclusion guards on it? I normally just put them in out of habbit and it is not going to change that. It just made me curious if this is something that compilers are allowed to do or if they have to do it, ie. is it standard? IMO inclusion guards should always be used so I don't know why they would want to allow you to get used to not doing it.

Recommended Answers

All 3 Replies

The paragraph is very poorly written. I understand what it is referring to, but the wording of the text is horrible and imprecise.

What it is referring to is the fact that template instantiations have internal linkage. This is to guarantee link-time satisfaction of the ODR (One-Definition Rule).

Normally, if you have a function declared in a header and defined (implemented) in a cpp file, then you must compile the cpp file once (as an object file) and link it to the rest of the object files to create your final executable (or dynamic library). If you were to link that object file twice, or have multiple definitions of the function in different source files, the linker would give you an error stating that you have multiple definitions for a given symbol (function-name).

With templates (function or class), the definitions usually appear in the header files, and the template is instantiated for each context in which it is used (i.e., as "needed"). This means that the same template instantiation could appear in multiple object files (from different source files that happen to contain the same template instantiations), which would seem to contradict the one-definition rule. To solve this issue, the compilers treat template instantiations as having internal linkage, meaning they are not "exported" from the object file, and thus, do not clash between each other when you link them together.

Note that many compilers do not exactly obey that rule, as often they do export the template instantiation symbols, but this is only to optimize away redundant code in the final executable.

Long story short, templates are not subject to the linkage part of the One-Definition Rules. The same is true for functions that are marked as inline or as static (or appear in unnamed namespaces), which all have internal linkage.

Does this mean that if I have a .h file with a templated class in it I don't need to have inclusion guards on it?

No. You must use inclusion guards still. This thing has nothing to do with "inclusions", in the sense of #include, which is why I think the paragraph is horribly phrased, they probably meant to say "linking together" instead of "the inclusion of".

The one-definition rule has two parts, it applies to compilation and linking. If you include the same header twice (with no guards), you get complaints about multiple declarations (for classes) and multiple definitions (for inline functions and function templates), which is the ODR for compilation. It is no different for templates. You cannot have the same template declaration or definition multiple times in the same translation unit (i.e., the source file and all its included headers, recursively).

See this wiki.

Thank you very much Mike

Does this mean that if I have a .h file with a templated class in it I don't need to have inclusion guards on it?

No.

Long story short, templates are not subject to the linkage part of the One-Definition Rules. The same is true for functions that are marked as inline or as static (or appear in unnamed namespaces), which all have internal linkage.

The linkage rules do not change for either templates or inline functions (or both). For instance, by default an inline function, or a function template instantiation has external linkage.

Instead, the IS has a special proviosion in the ODR rules for inline functions and templates.

3.2/5 There can be more than one definition of a class type, enumeration type, inline function with external linkage, class template, non-static function template, static data member of a class template, member function of a class template, or template specialization for which some template parameters are not specified 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; and
- etc. ...

Note: because of the proviso: 'provided that each definition appears in a different translation unit', include guards are still required.

A simple way to check this is to have two different translation units:

Header inlines.h

///////////////  inlines.h /////////////////

#ifndef INLINES_H_INCLUDED
#define INLINES_H_INCLUDED

#include <iostream>
#include <memory>

inline int external( const char* tag )
{
    static int same = 99 ;
    std::cout << tag << ": external::same is at "
               << std::addressof(same) << '\n' ;
    return same ;
}

template < typename T = int >
inline T also_external( const char* tag )
{
    static T same {} ;
    std::cout << tag << ": also_external<>::same is at "
               << std::addressof(same) << '\n' ;
    return same ;
}

static inline int internal( const char* tag )
{
    static int different = 0 ;
    std::cout << tag << ": internal::different is at "
               << std::addressof(different) << '\n' ;
    return different ;
}

template < typename T = int >
static inline T also_internal( const char* tag )
{
    static T different {} ;
    std::cout << tag << ": also_internal<>::different is at "
               << std::addressof(different) << '\n' ;
    return different ;
}


#endif // INLINES_H_INCLUDED

Implementation file test.cc

///////////////  test.cc /////////////////

#include "inlines.h"

static const int init = external("test.cc") +
                          also_external<>("test.cc") +
                          bool( std::cout << '\n' ) +
                          internal("test.cc") +
                          also_internal<>("test.cc") +
                          bool( std::cout << '\n' ) ;

Implementation file main.cc

///////////////  main.cc /////////////////

#include "inlines.h"

static const int init = external("main.cc") +
                          also_external<>("main.cc") +
                          bool( std::cout << '\n' ) +
                          internal("main.cc") +
                          also_internal<>("main.cc") +
                          bool( std::cout << '\n' ) ;

int main() {}

With gcc 4.9 i386, I get:

test.cc: external::same is at 0x48f2a4
test.cc: also_external<>::same is at 0x48f2a0

test.cc: internal::different is at 0x4c2034
test.cc: also_internal<>::different is at 0x4c2030

main.cc: external::same is at 0x48f2a4
main.cc: also_external<>::same is at 0x48f2a0

main.cc: internal::different is at 0x4c2024
main.cc: also_internal<>::different is at 0x4c2020

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.