对静态 constexpr 字符串的未定义引用(除非它是一个指针)
Undefined reference to static constexpr string (except if it's a pointer)
这项工作:
template<typename T> struct Something
{ static constexpr const char* str = "int"; };
int main()
{ std::cout << Something<int>::str << std::endl; }
但它没有:
template<typename T> struct Something
{ static constexpr const char str[] = "int"; };
int main()
{ std::cout << Something<int>::str << std::endl; }
gcc-4.8
说:"对Something<int>::str
的未定义引用"。
定义类外部的静态成员可以解决此错误:
template<typename T>
constexpr const char Something<T>::name[];
为什么它不是指针而需要数组?毕竟,两人都是static constexpr
成员。
或函数是odr使用的,则必须定义它。在某些情况下,对象和函数不是 odr 使用的,在这些情况下,您不必定义它们。无论静态类成员的声明是否具有初始值设定项,它仍然不是一个定义。在所有情况下,规则是,如果使用静态成员,则需要在封闭命名空间范围内进行类外定义。
直觉是"odr-used"意味着"链接器需要它的地址"。如果一个constexpr
变量只以需要其值的方式使用---而不是它的地址---那么它可以避免被 odr 使用。在这种情况下,编译器只是内联其值,不需要定义它,因为链接器不需要它的地址。const char* Something<int>::str
是这样,但不是const char Something<int>::str[]
。
"但他们是一样的!",你喊道。并非如此。因为当str
是const char*
时,它的值是字符串文字"int"
的地址。需要字符串文本的地址,但不需要str
本身的地址。前者是str
的值,它满足不被odr使用的要求;编译器可以内联它。但是当str
是一个const char[]
时,它的值是字符串文字"int"
本身。当你尝试使用 istream::operator<<
输出它时,它被隐式转换为const char*
。但是const char*
的值是字符串文字的地址,即Something<int>::str
的地址。因此,在这种情况下,Something<int>::str
是使用 odr;它的地址是必需的。
标准中有逻辑可用于精确确定变量何时被 odr 使用([basic.def.odr])。但我不会引用它,因为它是整个标准中最令人困惑的部分。我会说,在你有const char* Something<int>::str
的情况下,立即应用左值到右值的转换,这是它不被 odr 使用的条件之一;在你const char Something<int>::str[]
的情况下,数组到指针的转换会立即应用,这不满足条件。
N3485, §3.2 [basic.def.odr]/3 说:
名称显示为潜在计算表达式的变量 x EX 是 ODR 使用的,除非 X 是满足要求的对象 用于出现在常量表达式 (5.19) 中,ex 是 表达式 E 的潜在结果集,其中 左值到右值转换 (4.1) 应用于 e,或 e 是 丢弃值表达式(第 5 条)。
对于数组,它必须进行数组到指针的转换,以适合operator<<
的重载集。这在上面的文本中没有列出,因此数组str
是 odr 使用的。
§9.4.2 [class.static.data]/3 说(强调我的):
可以在类中声明文本类型的静态数据成员 使用constexpr说明符进行定义;如果是这样,其声明应 指定一个大括号或等于初始值设定项,其中每个初始值设定项子句 即赋值表达式是一个常量表达式。[ 注:在 在这两种情况下,成员都可能出现在常量表达式中。— 完 注意 ] 如果成员是 ODR 在程序中使用的 (3.2) 和命名空间范围定义应 不包含初始值设定项。
由于数组str
是 odr 使用的,因此必须在类外部定义它。指针str
不是 odr 使用的,因此不必使用。
- 函数向量_指针有不同的原型,我可以构建一个吗
- C++-试图将函数指针推回到另一个CPP文件中的矢量时出错
- 引用一个已擦除类型(void*)的指针
- 修改函数中的指针(将另一个指针作为参数传递)
- 如何将指针从一个void函数传递到另一个C++
- 如何在没有数据拷贝的情况下从指针创建一个Eigen VectorXd对象
- 如果C++对象的类在另一个boost模块中声明,如何使用boost将指向该对象的指针返回到python
- 为什么我们要为avl树实现返回一个指向节点的指针,而不是void函数
- 不明白迭代器,引用和指针失效,一个例子
- 为什么C++对链表中的下一个节点使用指针,而像 C# 或 Java 这样的语言只使用类 Node 的名称?
- int数据类型的指针指向的是什么,如果是一个类的私有数据成员,我们创建了该类的两个对象?
- 将指针分配给另一个指针时会发生什么情况?
- 将指向给定子类的指针从一个向量复制到另一个向量
- C++将一个指针分配给另一个指针时执行的类型检查
- 创建一个棋盘格或"Interweave"两个链接列表。IE 更改两个链表的指针
- 给定一个指向堆分配内存的指针,智能指针实现如何为其找到合适的释放函数?
- 取消引用结束指针到数组类型的一个
- 更改一个指针后更改乘法对象
- const和指针(一个节点示例)
- 从函数返回指针.一个指针的值未更新