c++模板类静态const变量成员作为映射键给出未定义引用
C++ template class static const variable member as map key gives undefined reference
我有一堆类,有一个静态成员是枚举值。我在别处有一个映射,这个enum是键。现在,如果我在函数中使用模板形参来访问map,我将得到一个未定义的引用。
为了说清楚,这里有一个简化的不工作的例子:
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"}};
主:fun<A<1337>();
给我(g++ 4.7):
undefined reference to `(anonymous namespace)::A<1337>::Type'
template<class T>
void fun()
{
auto key = T::Type;
cout << map_[key] << endl;
}
编译并打印1337
当您使用T::Type
时,您必须定义它:
template<int T>
struct A
{
static const int Type = T;
}
template <int T>
const int A<T>::Type;
是的,即使你在A<T>
内提供了它的内联初始化器!
你可能没有意识到这一点的原因,与你在第二种情况下不会遇到同样问题的原因是一样的—由于左值到右值的直接转换,该标准允许编译器优化在运行时引用Type
的需求,而不是在编译时挑选值。链接器不需要搜索定义,也不会得到错误。
[C++11: 9.4.2/2]:
静态数据成员在其类定义中的声明不是定义,并且可以是不完整类型,而不是cv限定的void。静态数据成员的定义应该出现在包含该成员的类定义的命名空间作用域中。在命名空间作用域的定义中,静态对象的名称数据成员应使用::操作符由其类名限定。静态数据成员定义中的初始化表达式位于其类的作用域中(3.3.7)。 [. .]
[C++11: 9.4.2/3]:
如果非易失性const static
数据成员是整型或枚举类型,则其在类定义中的声明可以指定大括号或相等初始化器,其中每个初始化器子句都是赋值表达式是常量表达式(5.19)。文字类型的static data
成员可以用constexpr
说明符在类定义中声明;如果是,其声明应指定大括号或等号初始化式,其中每个为赋值表达式的初始化式子句都是常量表达式。[注意:在这两种情况下,成员都可以出现在常量表达式中。 如果成员在程序中被odr(3.2)使用,则该成员仍应在命名空间作用域中定义,并且命名空间作用域定义不应包含初始化式。
[C++11: 3.2/2]:
[…]如果变量名出现在潜在求值表达式中,则不使用该变量,除非它是一个对象,满足出现在常量表达式(5.19)中的要求,并且立即应用左值到右值的转换(4.1)。 [. .]
这是因为std::map::operator[]
通过引用获取其参数,这使得您的变量odr使用(参见c++ 11标准的3.2/3段)。
简而言之,整个事情归结为这样一个事实:当编译器需要绑定一个对象的引用时,它需要知道对象的地址,这使得它不可能将该对象视为纯值并执行内联。
在这种情况下,您需要在全局命名空间范围内提供静态数据成员的定义,以便编译器知道该对象占用的存储区域(即它的地址是什么):template<int T>
const int A::Type;
c++ 11标准第9.4.2/3段:
如果非易失性
const
静态数据成员是整型或枚举类型,则在类中声明定义可以指定大括号或等号初始化式,其中每个初始化式子句都是赋值表达式是常量表达式(5.19)。[…成员仍然被定义在一个命名空间作用域中,如果它在程序中被odr(3.2)使用,并且命名空间作用域定义不允许使用包含一个初始化式。
另一方面,在程序的第一个版本中,您只使用静态数据成员的值,这意味着Type
没有被odr使用,并且不需要在命名空间范围内定义。
- 智能指针作为无序映射键,并通过引用进行比较
- 拥有映射的现代方法,该映射可以指向或引用已在堆栈上分配的不同类型的数据
- 从函数返回对映射的引用
- 为什么我不能返回带有透明函子的法线映射引用?
- 通过指针或引用将映射传递给函数?
- 将从std::映射中获取的std::pair引用传递给接受std::对引用的函数
- 映射/集迭代器不可取消引用 (C++) - 调试断言失败
- 取消引用映射迭代器时返回对临时的引用
- 有没有办法在不使用循环的情况下非具体地引用映射中的每个值?C++
- lambda 和映射,引用参数 - 编译错误
- 将引用对具有shared_ptr作为值的映射转换为对具有shared_ptr const 作为值的映射的引用
- Boost Intervocess:通过迭代通过从结构引用对象的映射进行迭代时
- 使用双迭代器引用映射中的列表
- 在消息映射中创建按钮和引用
- c++存储映射元组的引用,以便在另一个函数中使用
- 由引用集/映射迭代器传递的迭代器不可取消引用
- 本机C++类与 WinRT 组件(引用类)之间的自动映射
- 返回模板映射值作为引用
- SWIG 不可变标准::映射引用
- 同一个类使用的映射引用类