任何用 constexpr string_view 替换全局 const char[] 的陷阱?

Posted

技术标签:

【中文标题】任何用 constexpr string_view 替换全局 const char[] 的陷阱?【英文标题】:Any gotchas replacing global const char[] with constexpr string_view? 【发布时间】:2019-10-08 07:31:43 【问题描述】:

我们的团队正在使用具有 10 多年历史的 C++ 代码库,并且最近切换到了 C++17 编译器。所以我们正在寻找使我们的代码现代化的方法。在 YouTube 上的一次会议演讲中,我听到了建议,将 const char* 全局字符串替换为 constexpr string_view

由于我们的代码中有很多这样的const char* 全局字符串常量,我想问一下是否有任何我们需要注意的问题或潜在问题?

【问题讨论】:

【参考方案1】:

这些问题可能值得关注:

    std::string_view 不需要被null 终止。因此,如果您将一些const char* 替换为string_view 并将先前null 终止的char* 子字符串的构造替换为string_view 通过std::string_view::substr,则无法将底层指针传递给API需要一个null-终止的字符串。示例(没有 UB,但也很容易构建):

    void legacy(const char *str) 
       std::printf("%s\n", str);
    
    
    constexpr std::string_view sv1 = "abcde";
    constexpr std::string_view sv2 = sv1.substr(0, 2); // view on "ab"
    
    legacy(sv2.data()); // Not intended: prints "abcde" 
    

    虽然您可以从const char* 隐式构造std::string,但您不能使用std::string_view 来做到这一点。这个想法是,深层副本不应该在掩护下发生,而只有在明确要求时才发生。示例:

    std::map<std::string, int> m;
    constexpr std::string_view sv = "somekey";
    constexpr const char *old = "somekey";
    
    m[old] = 42; // works as expected
    m[sv] = 42; // fails to compile
    m[std::string(sv)] = 42; // be explicit, this is ok
    

    根据您项目中全局const char* 实例的现有使用情况,此行为可能需要在不同位置进行人工干预。

【讨论】:

非零终止肯定是一个陷阱-auch。现在我需要检查我们的 SV。我想你会用 std::string(sv).c_str() 代替传递给 API 吗? @darune 这是一个选项,但是应该检查 API 的生命周期假设,对吧?!如果你使用someLegacyFct(std::string(sv).c_str()) 并且这个后端以某种方式存储指针...... 这是正确的 - 仅在生命周期假设下 第二个问题是“幸运的是”对我们来说没什么大不了的。我们公司的框架有它自己的字符串类(我知道......),带有一个显式的const char* 构造函数。因此,在我们的案例中,从 string_view 显式构造 std::string 将是一致的。

以上是关于任何用 constexpr string_view 替换全局 const char[] 的陷阱?的主要内容,如果未能解决你的问题,请参考以下文章

constexpr string_view 比较

如何在 constexpr string_view 上使用 std::string_view::remove_prefix()

std::string_view 编译时散列

为啥这个 std::string_view 不是常量表达式?

如何从预处理器#if 指令调用 constexpr 函数?

我们应该尽可能使用 constexpr 吗?