为什么我在此处收到“子项具有其类型使用匿名命名空间的基数”警告

ale*_*uel 12 c++ gcc

我试图理解为什么在尝试编译此代码时收到警告 -Wsubobject-linkage:

\n

基本.hh

\n
#pragma once\n\n#include <iostream>\n\ntemplate<char const *s>\nclass Base\n{\npublic:\n  void print()\n  {\n    std::cout << s << std::endl;\n  }\n};\n
Run Code Online (Sandbox Code Playgroud)\n

孩子.hh

\n
#pragma once\n\n#include "base.hh"\n\nconstexpr char const hello[] = "Hello world!";\n\nclass Child : public Base<hello>\n{\n};\n
Run Code Online (Sandbox Code Playgroud)\n

主程序.cc

\n
#include "child.hh"\n\nint main(void)\n{\n  Child c;\n  c.print();\n  return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

跑步时g++ main.cc I get this warning:

\n
In file included from main.cc:1:\nchild.hh:7:7: warning: \xe2\x80\x98Child\xe2\x80\x99 has a base \xe2\x80\x98Base<(& hello)>\xe2\x80\x99 whose type uses the anonymous namespace [-Wsubobject-linkage]\n    7 | class Child : public Base<hello>\n      |       ^~~~~\n
Run Code Online (Sandbox Code Playgroud)\n

如果模板化值是 int 或将 child.hh 复制到 main.cc 中,则不会出现此警告\n我不明白这里的警告理由是什么,以及我应该从中理解什么。

\n

use*_*522 8

这个警告是适当的,只是措辞有点不清楚。

hello被声明constexpr,以及冗余const。变量const通常具有内部链接(有一些例外,例如变量模板、inline变量等)。

因此hello,模板参数 toBase<hello>是一个指向内部链接对象的指针。在每个翻译单元中,它将引用hello该翻译单元的不同局部。

因此,Base<hello>不同的翻译单元中会有不同的类型,因此,如果您包含child.hh多个翻译单元,您将违反 ODR onChild的定义,因此您的程序将具有未定义的行为。

警告正在告诉你这一点。使用“匿名命名空间”是不好的。它应该说类似“其类型指的是具有内部链接的实体”。但除此之外,它恰恰说明了问题。

仅当您使用对象的指针或引用作为模板参数时,才会发生这种情况。如果仅获取变量的值,则该变量是否具有内部链接或外部链接并不重要,因为类型Base<...>将由变量的值决定,而不是由变量的标识决定。


我不知道为什么您需要模板参数作为此处的指针,但如果您确实需要它并且实际上想要将该.hh文件包含在多个.cc文件中(直接或间接),那么您需要提供hello外部链接。那么你就会遇到一个问题,外部链接变量只能在一个翻译单元中定义,你可以通过创建变量来规避这一问题inline

inline constexpr char hello[] = "Hello world!";
Run Code Online (Sandbox Code Playgroud)

inline暗示外部链接(即使变量是const)并constexpr暗示const


如果您只需要字符串的值,而不需要变量的标识,那么从 C++20 开始,您可以传递 astd::array<char, N>作为模板参数:

#include<array>
#include<concepts>

/*...*/

template<std::array s>
requires std::same_as<typename decltype(s)::value_type, char>
class Base
{
public:
  void print()
  {
    std::cout << s.data() << std::endl;
  }
};

/*...*/

constexpr auto hello = std::to_array("Hello world!");
Run Code Online (Sandbox Code Playgroud)

有了这种联系就变得无关紧要了。写作Base<std::to_array("Hello world!")>也是可以的。

在 C++20 之前,确实没有任何好的方法来按值传递字符串作为模板参数。

即使在 C++20 中,您也可能需要考虑编写自己的std::array类似类,专门用于保存字符串。这样的类需要满足结构类型的要求。(std::string不满足它们。)另一方面,您可能希望通过使用概念来扩大允许的模板参数类型std::range,而std::range_iter_t不是std::array允许可以表示字符串的任何类型的范围。如果您不想要任何限制也auto可以。


use*_*570 6

如果child.hh包含在多个 TU 中,则会违反 ODR 并导致未定义的行为 NDR,如此处所述.

\n

此处提交了一个用于更好地措辞警告的新错误:

\n

Bug 106141 - 更好的警告措辞: \xe2\x80\x98Child\xe2\x80\x99 有一个基 \xe2\x80\x98Base<(& hello)>\xe2\x80\x99 ,其类型使用匿名命名空间 [-Wsubobject -连锁]

\n
\n

还有一个旧的错误提交为:

\n

Bug 86491 - 虚假且不可抑制的警告:\'YYY\' 有一个基 \'ZZZ\',其类型使用匿名命名空间

\n