我收到了 GCC 的子对象链接警告,但我不明白。可以使用以下代码演示该警告。
示例.h:
#ifndef EXAMPLE_H
#define EXAMPLE_H
static constexpr int value{1};
template <auto& N> struct Base {};
struct Foo : Base<value> {};
#endif
示例.cpp:
#include "example.h"
编译它会从 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> {};
| ^~~
神箭链接: https://godbolt.org/z/M5eqz9qK6
如果我将 example.h 的内容直接放入 example.cpp 中,那么它可以正常编译。 Clang 和 msvc 不会生成相同的警告。这段代码有问题吗,或者这是 GCC 中的一个错误?
这只是警告,不是错误。它是格式良好的 C++,如果您不使用
-Werror
,则可以编译。
但是,警告是合理的。
您将
Foo
的定义放入头文件中,这通常意味着它旨在包含在多个翻译单元中。
value
具有内部存储持续时间,因为它是非模板、非内联、非 extern
const 限定的变量。 (static
是多余的。)
因此,在每个翻译单元中
value
是一个不同的对象。并且因为 Base
采用引用作为模板参数,因此 Base<value>
是 Base
在不同翻译单元中的不同特化,因为模板参数引用每个中的不同对象。
因此,如果您在两个不同的翻译单元中包含标头,则程序将出现未定义的行为,因为
Foo
的定义违反了单定义规则,该规则不仅要求相同的两个定义模板由完全相同的标记序列组成,而且在定义中查找的所有名称都引用每个定义中的相同实体(有一些例外情况在此不适用)。
因此,警告告诉您,您的标头永远不能包含在多个翻译单元中,而不会导致未定义的行为,这可能不是有意的。