C++ C++;模板类静态常量变量成员作为映射键提供未定义的引用

C++ C++;模板类静态常量变量成员作为映射键提供未定义的引用,c++,templates,c++11,g++,undefined-reference,C++,Templates,C++11,G++,Undefined Reference,我有一堆类,它们有一个静态成员,是枚举值。我在其他地方有一张地图,上面有这个枚举作为键。现在,如果我在函数中使用模板参数来访问映射,我会得到一个未定义的引用 为了清楚起见,下面是一个简化的非工作示例: template<int T> struct A { static const int Type = T; } template<class T> void fun() { cout << map_[T::Type] <<

我有一堆类,它们有一个静态成员,是枚举值。我在其他地方有一张地图,上面有这个枚举作为键。现在,如果我在函数中使用模板参数来访问映射,我会得到一个未定义的引用

为了清楚起见,下面是一个简化的非工作示例:

template<int T>
struct A
  {
    static const int Type = T;
  }

template<class T>
void fun()
  {
    cout << map_[T::Type] << endl;
  }

map<int, string> map_{{1337, "1337"}};
模板
结构A
{
静态常数int Type=T;
}
模板
虚无乐趣()
{

cout使用
T::Type
时,必须定义它:

模板
结构A
{
静态常数int Type=T;
}
模板
常量int A::类型;
是的,即使您在
A
中内联提供了它的初始化器

您可能没有意识到这一点的原因,与您在第二种情况下没有遇到相同问题的原因相同-由于左值到右值的直接转换,标准允许编译器优化要求,以便在运行时引用
类型
,而不是在编译时选择值然后,r就不需要搜索定义,并且不会得到任何错误


[C++11:9.4.2/2]:
静态数据成员在其类定义中的声明不是定义,可能是除cv qualified void之外的不完整类型。静态数据成员的定义应出现在包含该成员的类定义的命名空间范围中。在命名空间范围的定义中静态文件的名称 数据成员应使用::运算符通过其类名进行限定。静态数据成员定义中的初始值设定项表达式在其类的范围内(3.3.7)。[…]

[C++11:9.4.2/3]:
如果非易失性
const static
数据成员为整型或枚举型,则其在类定义中的声明可以指定一个大括号或相等的初始值设定项
,其中的每个初始值设定项子句都是赋值表达式 是常量表达式(5.19)。可以在类定义中使用
constexpr
说明符声明文本类型的
静态数据
成员;如果是,其声明应指定一个大括号或相等的初始值设定项,其中作为赋值表达式的每个初始值设定项子句都是常量表达式。[注意:在这两种情况下,成员可能出现在常量表达式中。-结束注意]如果在程序中使用odr(3.2),则该成员仍应在名称空间范围中定义,并且名称空间范围定义不应包含初始值设定项。

[C++11:3.2/2]:
[…]如果一个变量的名称显示为一个可能的求值表达式,则使用odr,除非它是一个满足在常量表达式(5.19)中出现的要求的对象,并且立即应用左值到右值的转换(4.1)。[…]

这是因为引用了它的参数,从而使用了变量odr(参见C++11标准的第3.2/3段)

简而言之,整个过程归结为这样一个事实:当编译器需要绑定一个对象的引用时,它需要知道该对象的地址,这使得它不可能像对待纯值一样对待该对象并执行内联

在这种情况下,您需要在全局命名空间范围内提供静态数据成员的定义,以便编译器知道该对象占用的存储区域(即其地址):

模板
常量int A::类型;
根据C++11标准第9.4.2/3段:

如果非易失性
const
静态数据成员为整型或枚举类型,则其在类中的声明 定义可以指定一个大括号或相等初始值设定项,其中每个初始值设定项子句都是赋值表达式 是一个常量表达式(5.19)。[…]仍应定义该成员 如果在程序中使用odr(3.2),则在名称空间范围中,并且名称空间范围定义不应 包含初始值设定项


另一方面,在程序的第一个版本中,您只使用了静态数据成员的值,这意味着没有使用odr,也不需要在名称空间范围中定义。

@hmjd我复制了/通过了您的链接,编译了,得到了错误。我在Debian x64上使用了g++4.7.2-5。您的链接似乎使用了g++4.7.2…我真的无法解释我认为@Lightness有。
fun<A<1337>();
undefined reference to `(anonymous namespace)::A<1337>::Type'
template<class T>
void fun()
  {
    auto key = T::Type;
    cout << map_[key] << endl;
  }
template<int T>
struct A
{
   static const int Type = T;
}

template <int T>
const int A<T>::Type;
template<int T>
const int A::Type;