gpt4 book ai didi

c++ - header 中的字符串——这是否违反了 ODR?

转载 作者:可可西里 更新时间:2023-11-01 16:09:43 26 4
gpt4 key购买 nike

考虑以下具有两个编译单元的程序。


// a.hpp

class A {
static const char * get() { return "foo"; }
};

void f();

// a.cpp

#include "a.hpp"
#include <iostream>

void f() {
std::cout << A::get() << std::endl;
}

// main.cpp

#include "a.hpp"
#include <iostream>

void g() {
std::cout << A::get() << std::endl;
}

int main() {
f();
g();
}

出于某种原因需要创建全局字符串常量是很常见的。以完全天真的方式执行此操作会导致链接器问题。通常,人们将声明放在头文件中,将定义放在单个编译单元中,或者使用宏。

我的印象是这种使用函数的方式(如上所示)是“好的”,因为它是一个 inline 函数并且链接器消除了生成的任何重复拷贝, 使用这种模式编写的程序似乎运行良好。但是,现在我怀疑它是否真的合法。

函数 A::get 在两个不同的翻译单元中被 ODR 使用,但它是隐式内联的,因为它是一个类成员。

[basic.def.odr.6] 中声明:

There can be more than one definition of a ... inline function with external linkage (7.1.2)... in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. Given such an entity named D defined in more than one translation unit, then
- each definition of D shall consist of the same sequence of tokens; and
- in each definition of D, corresponding names, looked up according to 3.4, shall refer to an entity defined within the definition of D, or shall refer to the same entity, after overload resolution (13.3) and after matching of partial template specialization (14.8.3), except that a name can refer to a non-volatile const object with internal or no linkage if the object has the same literal type in all definitions of D, and the object is initialized with a constant expression (5.19), and the object is not odr-used, and the object has the same value in all definitions of D; and
- in each definition of D, corresponding entities shall have the same language linkage; and
- ... (more conditions that don't seem relevant)

If the definitions of D satisfy all these requirements, then the program shall behave as if there were a single definition of D. If the definitions of D do not satisfy these requirements, then the behavior is undefined.

在我的示例程序中,两个定义(每个翻译单元中一个)分别对应相同的标记序列。 (这也是我原本觉得还可以的原因。)

但是,不清楚是否满足第二个条件。因为,名称 “foo” 可能不对应于两个编译单元中的同一个对象——它可能是每个编译单元中的“不同”字符串文字,不是吗?

我尝试更改程序:

  static const void * get() { return static_cast<const void*>("foo"); }

以便它打印字符串文字的地址,我得到相同的地址,但我不确定是否一定会发生。

它是否属于“...应引用在 D 的定义中定义的实体”? "foo" 是否被认为是在 A::get 中定义的?看起来可能是这样,但据我非正式理解,字符串文字最终会导致编译器发出某种全局 const char[] ,它位于可执行文件的特殊段中。该“实体”是否被认为在 A::get 内,还是不相关?

“foo” 是否甚至被视为“名称”,或者术语“名称”是否仅指代有效的 C++“标识符”,如可用于变量或函数?一方面它说:

[basic][3.4]
A name is a use of an identifier (2.11), operator-function-id (13.5), literal-operator-id (13.5.8), conversion- function-id (12.3.2), or template-id (14.2) that denotes an entity or label (6.6.4, 6.1).

标识符是

[lex.name][2.11]
An identifier is an arbitrarily long sequence of letters and digits.

所以看起来字符串文字不是名称。

另一方面,在第 5 节

[expr.prim.general][5.1.1.1]
A string literal is an lvalue; all other literals are prvalues.

一般来说,我认为 lvalues 是有名字的。

最佳答案

你最后的论点是胡说八道。 “foo” 在语法上什至不是一个名称,而是一个字符串文字。字符串文字是左值,一些具有名称的左值并不意味着字符串文字是或具有名称。代码中使用的字符串文字不违反 ODR。

实际上,在 C++11 之前,强制要求跨 TU 的内联函数的多个定义中的字符串文字指定相同的实体,但多余的且大部分未实现的规则已被 CWG 1823 删除.

Because, the name "foo" might not correspond to the same object in the two compilation units -- it's potentially a "different" string literal in each, no?

正确,但这无关紧要。因为 ODR 不关心具体的参数值。如果您确实设法以某种方式获得不同的例如要在两个 TU 中调用函数模板特化,这会产生问题,但幸运的是字符串文字是无效的模板参数,所以你必须要聪明点。

关于c++ - header 中的字符串——这是否违反了 ODR?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37901693/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com