【问题标题】:use `const char thing[] = "foo";` in a header file?在头文件中使用`const char thing[] = "foo";`?
【发布时间】:2014-09-22 17:54:49
【问题描述】:

我想创建一个常量全局字符数组,这样

  • 可用于多个翻译单元。
  • 数组的长度是从用于初始化它的字符串文字推导出来的。
  • 该字符串文字在我的源文件中仅存在一次(如果可能,在目标文件中)。
  • 所有翻译单元都可以在编译时访问长度。
  • 当多个翻译单元链接在一起时,没有 ODR 违规。

理论上,这应该可以通过使用const char[] 并将减速/定义放在头文件中以强制数据/符号进入 COMDAT 部分的方式实现,但我不知道标准(甚至任何编译器)都支持。

附言假设使用的任何习语都将用于许多文件中的数百到数千个常量。


编辑:我所知道的给出所有要点的“最干净”的解决方案是:

template<bool> struct data_ {
  static const char kFoo[];
};

template<> const char data_<true>::kFoo[] = "bar\0other\0stuff\0";
typedef data_<true> data;


#include <stdio.h>

template<typename T, int N>
void Print(T(&var)[N]) { printf("%d %s\n", N, var); }

int main() { Print(data::kFoo); return 0; }

还是很丑的。


OTOH 如果我只是扔掉 3b(保证相同的存储模块内联),那么这项工作:

const char kFoo[] = "bar\0other\0stuff\0";

因为默认情况下它具有内部链接。一个好的链接器可以合并这些,但此时你不能说地址/标识符的相等性之间的关系(即不要将其转换为指针并将其用作标识)。但这是一个可能几乎一直都是良性的警告。

【问题讨论】:

  • 在头文件中,这样的变量(const而不是extern)会得到内部链接,这意味着每个翻译单元中会有不同的对象。
  • 我认为您可以使用类模板的静态数据成员或内联函数中的静态局部变量来满足这些要求。
  • 另一个有趣的想法可能是将字符串文字地址和长度分开存储,并让链接器合并这些字符串文字。
  • const & not extern 失败点 3b。模板解决方案很丑陋,需要重复名称和大部分类型。函数静态解决方案需要自动返回类型(c++14,还没有)或失败点 4。我不确定单独的存储如何获得所有点的 2、3a 和 4。
  • 虽然我通常不提倡使用 Singleton 模式,但制作一个封装字符串文字的 Singleton 类应该满足您的所有要求,但可能要求 4 除外。)“所有翻译单元可以在编译时访问长度”,但可能仍有一些方法可以强制执行。

标签: c++ dry one-definition-rule


【解决方案1】:

标题:

#define LITERAL "Hello, world"
extern char const literal[sizeof LITERAL];

一个源文件:

char const literal[] = LITERAL;

仍然不能保证任何特定的编译器/链接器只制作字符串文字的一份副本(但它确实保证&amp;literal[0] 在所有单元中都是相同的要求)。

【讨论】:

  • 宏和跨文件或模板的重复标识符以及单个文件中的重复标识符... :-(
  • @BCS 嗯?这满足您列出的所有条件,我不知道您所说的“单个文件中的重复标识符”是什么意思(这不是您的条件之一)
  • 模板解决方案(包含在问题中)声明的标识符显示在样板文件中的多个位置(就像您的解决方案一样),但它们都可以放在一个文件中。 tl;博士;我现在知道了几种方法来完成这项工作,但都是丑陋的。
  • 我的只有两个地方的标识符(一个 .h 文件和一个 .cpp 文件)。您可以使用预处理技巧将其简化为一个,但您没有将其列为要求,我不明白您为什么认为它会有所作为。
  • 你的确实符合我所说的所有要求。然而,恕我直言,它仍然不优雅。或者换一种说法:我不喜欢它,或任何其他选项。我很有可能不会喜欢不需要更改语言的任何东西。
【解决方案2】:

从 C++17 开始可以在头部写:

inline char const thing[] = "foo";

符合所有标准。

注意: inline 变量具有外部链接,除非明确声明为 staticconst 变量默认为内部链接的规则仅适用于非内联变量。

【讨论】:

  • 你仍然需要extern,因为这是一个变量而不是一个函数(否则它们可能不会被链接器合并)。
  • @DanielH thing 在我的代码中已经有外部链接(参见 [basic.link]/4.1)
  • 你是对的。我记得类似的东西,但找不到参考,所以假设我记错了。原来它就在我看的下面。
【解决方案3】:

你不想

const char * const LITERAL="foo";

【讨论】:

  • 不,我认为这要么违反 ODR,要么导致在二进制文件中多次存储“foo”。此外,代码在编译时无法获取数组的大小。 (考虑字符串是否包含\0 或长度用作模板参数。)
  • 无 ODR 违规,LITERAL 具有内部链接。
  • 命中除一个之外的所有标记,假设工具链已正确配置为合并重复的字符串文字(一个不常见的配置选项)。缺少的一个:从字符串文字推导出的数组长度。 IE。 sizeof(LITERAL) 的应用程序不会给出 OP 正在寻求的结果。
  • 为了困难,我要说“恰好一次”的要求是来自不同单元的字符串之间的指针比较被明确定义为相等(需要一个符合要求的编译器来确保这是真的)
猜你喜欢
  • 2019-07-20
  • 2010-12-10
  • 1970-01-01
  • 2021-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-17
相关资源
最近更新 更多