我有以下示例代码,它使用字符串文字作为模板参数,这样基类模板就可以访问该字符串。
代码编译,但我得到一个警告,我不完全理解:
警告:'ns :: bar :: type'有一个基数'ns :: base<((const char *)(& ns :: bar :: name))>'其类型使用匿名命名空间[启用 默认]
下面的工作示例代码:
// "test.h"
#pragma once
namespace ns
{
template <char const* str>
struct base
{
const char *name() const { return str; }
};
namespace bar
{
static constexpr char name[] = "bar";
struct type : base<name> {}; // <-- this line here
}
}
// main.cpp
#include <iostream>
#include "test.h"
int main()
{
ns::bar::type f;
std::cout << f.name() << std::endl;
return 0;
}
所以我的问题是:
(注意这是gcc 4.7.2)
答案 0 :(得分:7)
问题在于static constexpr char name[] = "bar";
具有内部联系。
标题中定义了内部链接的任何类型在包含标题的每个文件中都是不同的类型。
这很少是意图 - 因此警告。
在源文件中完成此操作时没有警告的原因是因为该类型不能被多个文件引用 - 因此它将始终是一种类型。
答案 1 :(得分:3)
警告是因为name
在每个包含test.h
的源文件中都有不同的地址。因为它有内部链接(static
),每个翻译单元将获得自己的副本;它们不会被链接器统一起来。这意味着您的代码等同于:
template<int> struct base { ... };
static constexpr int val = some_value_different_in_every_source_file;
struct type: base<val> {};
您的代码是的合法,但如果您在另一个源文件中包含test.h
,那么它将违反单一定义规则:
3.2一个定义规则[basic.def.odr]
[...]
6 - [...] [...]提供的类类型可以有多个定义:[...]
- 在
,则只有内部链接或没有链接[仅]D
的每个定义中,相应的名称[...]可以引用const对象 如果[...]使用对象的值(但不是地址)[...]
您在类类型的定义中使用具有内部链接的对象的地址,因此在多个翻译单元中使用它是未定义的行为。