I have these three files
// foo.h
#pragma once
template <typename T> const T foo;
template <>
const int foo<int> = 1;
// a.cpp
#include "foo.h"
int main() {}
// b.cpp
#include "foo.h"
If I build these with GCC 7.5.0 on Linux it works fine:
$ g++ -std=c++17 a.cpp b.cpp -o out
$
However with Apple Clang 12.0.0 on Mac it gives this error:
$ clang++ -std=c++17 a.cpp b.cpp -o out
duplicate symbol 'foo<int>' in:
/var/folders/g5/8twmk1xj481_6btvppyw5j4h0000gp/T/a-62bdde.o
/var/folders/g5/8twmk1xj481_6btvppyw5j4h0000gp/T/b-ea4997.o
ld: 1 duplicate symbol for architecture x86_64
Should this be an error, or is it a bug in Clang?
RedFrog's answer is correct - this violates the One Definition Rule (ODR) but it doesn't really explain why it violates the ODR. In C++ global const
variables have internal (static
) linkage, which should result in a separate instance of the variable for each compilation unit, so ODR isn't violated. However it turns out that const
does not cause template variables to have internal linkage:
The const qualifier used on a declaration of a non-local non-volatile non-template (since C++14) non-inline (since C++17) variable that is not declared extern gives it internal linkage.
There are a few ways to fix this.
static
You can use static
on template variables to gives them internal linkage:
Any of the following names declared at namespace scope have internal linkage:
variables, variable templates (since C++14), functions, or function templates declared static;
Edit: I don't recommend using this technique. If you apply static
to the declaration and the specialisations then it works fine in Clang but GCC (as of now at least) complains that
error: explicit template specialization cannot have a storage class
If you set static
only on the declaration, then it compiles in GCC but you get duplicate symbol errors with Clang.
namespace
Putting template variables in an anonymous namespace also gives them internal linkage.
In addition, all names declared in unnamed namespace or a namespace within an unnamed namespace, even ones explicitly declared extern, have internal linkage.
inline
This is a bit different. Template variables that are declared inline
but not static
still have external linkage, but are allowed to have more than one definition.
An inline function or variable (since C++17) with external linkage (e.g. not declared static) has the following additional properties:
- There may be more than one definition of an inline function or variable (since C++17) in the program as long as each definition appears in a different translation unit and (for non-static inline functions and variables (since C++17)) all definitions are identical. For example, an inline function or an inline variable (since C++17) may be defined in a header file that is #include'd in multiple source files.
- It must be declared inline in every translation unit.
- It has the same address in every translation unit.
This means that inline
for variables behaves like inline
for functions.
If in doubt, inline
is probably the best option to go for. In the very simple tests I've done they all produced identical output, even at -O0, but in theory inline
guarantees that there is only one copy of the variable in your program because it still has external linkage, whereas the other methods don't. Maybe.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With