我试图理解为什么在尝试编译此代码时收到警告 -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};\nRun 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};\nRun 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}\nRun Code Online (Sandbox Code Playgroud)\n跑步时g++ main.cc I get this warning:
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 | ^~~~~\nRun Code Online (Sandbox Code Playgroud)\n如果模板化值是 int 或将 child.hh 复制到 main.cc 中,则不会出现此警告\n我不明白这里的警告理由是什么,以及我应该从中理解什么。
\n这个警告是适当的,只是措辞有点不清楚。
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可以。
如果child.hh包含在多个 TU 中,则会违反 ODR 并导致未定义的行为 NDR,如此处所述.
此处提交了一个用于更好地措辞警告的新错误:
\n\n还有一个旧的错误提交为:
\nBug 86491 - 虚假且不可抑制的警告:\'YYY\' 有一个基 \'ZZZ\',其类型使用匿名命名空间。
\n| 归档时间: |
|
| 查看次数: |
1187 次 |
| 最近记录: |