I am trying to understand why I get a warning -Wsubobject-linkage when trying to compile this code:
base.hh
#pragma once
#include <iostream>
template<char const *s>
class Base
{
public:
void print()
{
std::cout << s << std::endl;
}
};
child.hh
#pragma once
#include "base.hh"
constexpr char const hello[] = "Hello world!";
class Child : public Base<hello>
{
};
main.cc
#include "child.hh"
int main(void)
{
Child c;
c.print();
return 0;
}
When running g main.cc
I get this warning:
In file included from main.cc:1:
child.hh:7:7: warning: ‘Child’ has a base ‘Base<(& hello)>’ whose type uses the anonymous namespace [-Wsubobject-linkage]
7 | class Child : public Base<hello>
| ^~~~~
This warning does not occur if the templated value is an int or if child.hh is copied in main.cc I do not understand what justifies this warning here, and what I'm supposed to understand from it.
CodePudding user response:
The warning is appropriate, just worded a bit unclear.
hello
is declared constexpr
, as well as redundantly const
. A const
variable generally has internal linkage (with some exceptions like variable templates, inline
variables, etc.).
Therefore hello
in the template argument to Base<hello>
is a pointer to an internal linkage object. In each translation unit it will refer to a different hello
local to that translation unit.
Consequently Base<hello>
will be different types in different translation units and so if you include child.hh
in more than one translation unit you will violate ODR on Child
's definition and therefore your program will have undefined behavior.
The warning is telling you that. The use of "anonymous namespace" is not good. It should say something like "whose type refers to an entity with internal linkage". But otherwise it is exactly stating the problem.
This happens only if you use a pointer or reference to the object as template argument. If you take just the value of a variable, then it doesn't matter whether that variable has internal or external linkage since the type Base<...>
will be determined by the value of the variable, not the identity of the variable.
I don't know why you need the template argument to be a pointer here, but if you really need it and you actually want to include the .hh
file in multiple .cc
files (directly or indirectly), then you need to give hello
external linkage. Then you will have the problem that an external linkage variable may only be defined in one translation unit, which you can circumvent by making the variable inline
:
inline constexpr char hello[] = "Hello world!";
inline
implies external linkage (even if the variable is const
) and constexpr
implies const
.
CodePudding user response:
If child.hh
is included in multiple TUs, it would violate ODR and lead to undefined behavior NDR as explained here.
A new bug for better wording of the warning has been submitted here:
There is also an old bug submitted as: