Search code examples
c++gccg++

Is this subobject-linkage warning a GCC bug?


I am getting a subobject-linkage warning with GCC that I do not understand. The warning can be demonstrated with the following code.

example.h:

#ifndef EXAMPLE_H
#define EXAMPLE_H

static constexpr int value{1};

template <auto& N> struct Base {};
struct Foo : Base<value> {};

#endif

example.cpp:

#include "example.h"

Compiling this yields the following output from GCC:

/app/example.h:7:8: error: 'Foo' has a base 'Base<value>' which has internal linkage [-Werror=subobject-linkage]
    7 | struct Foo : Base<value> {};
      |        ^~~

Godbolt link: https://godbolt.org/z/M5eqz9qK6

If I put the contents of example.h directly into example.cpp then it compiles fine. Clang and msvc do not generate this same warning. Is there an issue with this code, or is this is a bug in GCC?


Solution

  • This is only a warning, not an error. It is well-formed C++ and would compile if you didn't use -Werror.

    However, the warning is reasonable.

    You are putting Foo's definition in a header file, which usually means that it is intended to be included in multiple translation units.

    value has internal storage duration, because it is a non-template, non-inline, non-extern const-qualified variable. (The static is redundant.)

    As a consequence, in each translation unit value is a different object. And because Base takes a reference as template argument, then Base<value> are different specializations of Base in different translation units, because the template parameter references different objects in each.

    Then, if you include the header in two different translation units, the program will have undefined behavior, because the definitions of Foo violate the one-definition rule as they are, roughly said, not semantically identical. The compiler has no way to decide whether Foo is supposed to have Base<value1> or Base<value2> as base class (where value1 and value2 are invented names for the two instances of value in the different translation units).

    More precisely the one-definition rule not only requires that two definitions of the same class are formed by the exact same token sequence, but also that all names looked up in the definition refer to the same entity in each definition (with some exceptions that don't apply here). In fact, that value is used as by-reference template argument isn't even relevant. The difference in lookup, coupled with value being odr-used is sufficient to violate ODR. (See [basic.def.odr]/13.9 for the precise rule.)

    So, the warning is telling you that your header can never be included in more than one translation unit without causing undefined behavior, which likely is not intended.