Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Duplicate symbols with global template variable using Clang

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?

like image 620
Timmmm Avatar asked Sep 01 '25 21:09

Timmmm


1 Answers

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.

Internal Linkage using 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.

Internal Linkage using 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.

Disable ODR using 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.

like image 117
Timmmm Avatar answered Sep 03 '25 22:09

Timmmm