这个子对象链接警告是GCC错误吗?

oxalkeyp  于 6个月前  发布在  其他
关注(0)|答案(1)|浏览(56)

我在使用GCC时收到一个我不理解的子对象链接警告。该警告可以用以下代码演示。
示例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"


编译此代码会从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链接:https://godbolt.org/z/M5eqz9qK6
如果我把example.h的内容直接放到example.cpp中,那么它编译得很好。Clang和msvc不会生成同样的警告。这段代码有问题吗?或者这是GCC中的一个bug?

p8ekf7hl

p8ekf7hl1#

这只是一个警告,而不是一个错误。它是格式良好的C++,如果你不使用-Werror,它也可以编译。
但警告是合理的。
您正在将Foo的定义放在一个头文件中,这通常意味着它打算包含在多个翻译单元中。
value具有内部存储持续时间,因为它是一个非模板、非内联、非extern常量限定的变量。(static是冗余的。)
因此,在每个翻译单元中,value是不同的对象。由于Base将引用作为模板参数,因此Base<value>Base在不同翻译单元中的不同特化,因为模板参数在每个翻译单元中引用不同的对象。
然后,如果你在两个不同的翻译单元中包含header,程序将有未定义的行为,因为Foo的定义违反了单定义规则,粗略地说,编译器无法决定Foo是否应该将Base<value1>Base<value2>作为基类(其中value1value2是不同翻译单位中value的两个示例的发明名称)。
更准确地说,单定义规则不仅要求同一个类的两个定义由完全相同的标记序列构成,而且要求在定义中查找的所有名称都引用每个定义中的同一个实体(有些例外在这里不适用)。事实上,value被用作引用模板参数是不相关的。查找中的差异,再加上value是odr使用的,就足以违反ODR。(有关精确规则,请参见[basic.def.odr]/13.9。)
因此,警告告诉你,你的头永远不能包含在一个以上的翻译单元中,而不会导致未定义的行为,这可能不是故意的。

相关问题