【问题标题】:Ensure that char pointers always point to the same string literal确保 char 指针始终指向相同的字符串文字
【发布时间】:2019-11-14 21:41:46
【问题描述】:

给定代码

// somewhere in the program
const char* p1 = "Hello World";

// somewhere else in the program
const char* p2 = "Hello World";

有没有办法确保p1 == p2 在整个程序/库中始终得到满足?我的意思是p1p2 总是指同一个字符串字面量。

背后的原因

我想要实现的是使用const char* 作为std::map<const char*, something> 的键。我有一个宏

#define nameof(id) #id

模仿 C# 中 nameof 关键字的行为(我知道这已经存在缺陷),我想用它来访问类似结构的注册表,例如

void foo()
{
    auto x = getMapping(nameof(foo));
}

// different place in code

void registerFoo(something x)
{
    setMapping("foo", x);
}

【问题讨论】:

标签: c++ c++17


【解决方案1】:

正如 Barry 在 their answer 中显示的那样,您想要的行为并不能得到保证。您将不得不支付字符串比较的成本,但您至少可以避免任何内存分配或使用std::string_view 编写比较器。 std::string_view 是一个字符串的轻量级视图,它包含一个指向字符串数据的指针和字符串的大小,它有一个内置的operator <,可以进行字典比较。这会将您的地图更改为

std::map<std::string_view, something>

【讨论】:

  • 绝对是正确的答案(除了在一些罕见且我不知道的情况下,真正需要身份的情况下)
【解决方案2】:

没有这样的要求。 [lex.string]/15:

是否所有字符串文字都是不同的(即,存储在不重叠的对象中)以及字符串文字的连续评估是否产生相同或不同的对象是未指定的。

你能做的最好的事情是assert() 或者只是避免重复自己并将事情粘在一个函数中:

char const* my_literal() { return "Hello World"; }

char const* p1 = my_literal();
char const* p2 = my_literal();

【讨论】:

  • 嗯是这么想的。我已经更新了我的问题。对此有什么想法吗?
  • “连续评估”位不是意味着my_literal 可能在每次调用中产生不同的值吗? (即使是 constexpr!)
  • @DavisHerring 是的...措辞问题?否则,这是一个相当敌对的,并且容易违反 odr 的,阅读:-)
  • @Barry:鉴于禁止字符串文字相关的模板参数和 ODR 基于令牌的检查,我实际上不知道如何从这个怪癖中形成 ODR 违规。 (这并不意味着它不疯狂。)
  • @DavisHerring 是的,也许不是。我的默认设置是假设违反 odr。
【解决方案3】:

相同的文字字符串不保证相同, 但是当您使用 MACRO 创建字符串时,您可以将其更改为返回相同的字符串。

gcc/clang 有一个扩展允许从文字字符串构建 UDL:

template<typename Char, Char... Cs>
struct CsHelper
{
    static constexpr const Char s[] = {Cs..., 0}; // The unique address
};

// That template uses the extension
template<typename Char, Char... Cs>
constexpr auto operator"" _cs() -> const Char (&)[1 + sizeof...(Cs)] {
    return CsHelper<Char, Cs...>::s;
}

然后

#define nameof(id) #id ## _cs

如果您不能使用扩展名,请参阅String-interning at compiletime for profiling 的回答以使用 MAKE_STRING 宏(对于接受的字符串长度确实更冗长,并且硬编码限制)。

【讨论】:

    【解决方案4】:

    不要求具有相同文本的两个字符串字面量是同一个对象。因此,”Hello world” 的两次提及可能会或可能不会引用内存中的单个字符串。这意味着

    const char* p1 = "Hello World";
    const char* p2 = "Hello World";
    

    不一定使p1 等于p2。为此,您必须将其中一个设置为等于另一个:

    const char* p2 = p1;
    

    但是这些指针中的任何一个都可以修改,而另一个指针不会跟踪该更改。为确保无法进行此类更改,请将指针设为 const:

    const char* const p1 = "Hello World";
    const char* const p2 = p1;
    

    或者,如果需要修改 p1,请将 p2 设为引用:

    const char* p1 = "Hello World";
    const char*& p2 = p1;
    

    现在p2 将指向p1 指向的任何位置。

    【讨论】:

    • 问题是,p1p2 互不认识,
    • @Timo — 如果您在问题中这么说,我就不会浪费我的时间了。
    • 很抱歉浪费您的时间,但这就是我用代码编写 cmets 的原因。
    • @Timo — cmets 不会这么说。
    猜你喜欢
    • 2019-01-15
    • 2015-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多