首页 > 解决方案 > 从函数返回 string_view

问题描述

我正在编写很多 string_view 擅长的解析器代码,并且喜欢这种类型。我最近阅读了 ArthurO'Dwyer 的文章std::string_view is a borrow type,他的结论是 string_view (和其他“借用类型”)只要它们“......仅作为函数参数和 for-loop 出现”就可以使用控制变量。” (有几个例外)。

但是,我最近开始使用 string_view 作为将枚举转换为字符串(我经常使用)的函数的返回值,例如Compiler Explorer

#include <iostream>
#include <string>
#include <array>
#include <algorithm>

enum class Color
{
    red, green, blue, yellow,
    last // Must be kept last
};
constexpr std::string_view toString(Color color); 

// The rest would normally be in a .cpp file

using cts = std::pair<Color, std::string_view>;
constexpr std::array colorNames = {cts{Color::red, "red color"},
                            cts{Color::green, "green color"},
                            cts{Color::blue, "blue color"},
                            cts{Color::yellow, "yellow color"}};

static_assert(colorNames.size() == static_cast<size_t>(Color::last));

constexpr std::string_view toString(Color color)
{
    // Normally calling a library function (which also checks for no match), instead of this:
    return std::ranges::find(colorNames, color, &cts::first)->second;
}

int main()
{
    auto s1 = toString(Color::green);
    auto s2 = toString(Color::blue);

    std::cout << s1 << ' ' << s2 << std::endl;
}

我这样做的原因是:

  1. 通过将它作为 string_view 存储在数组中,我可以使整个表 constexpr。
  2. 通过直接返回string_view,不需要转换字符串表示,因此整个函数可以是constexpr,或者至少避免创建不必要的字符串,即使使用非constexpr参数调用。
  3. 使用表 constexpr 的一个副作用是我可以使用 static_assert 检查枚举的所有元素是否都在表中,这对于捕获枚举的添加非常有用。我真的不喜欢将“最后一个”枚举值放在那里,但我没有看到更好的解决方案。

所以我的问题是真的,以这种方式返回 string_view 是不安全的(或 UB),还是我可以良心继续这样做?

或者,是否有更好(更快/更安全)的方法来解决这个枚举到字符串的一般问题?

补充:在阅读了 G. Sliepen 的非常好的答案后,我想在他的答案中添加我的评论:我也经常有相反的功能,例如:

constexpr Color fromString(string_view str)
{
  // No-match handling omitted
  return std::ranges::find(colorNames, color, &cts::second)->first;
}

在这些情况下,我确实需要将翻译作为单独的表格,以便两个函数都可以使用它。但在许多其他情况下,包含 switch 语句的函数是最简单和最好的。

标签: c++c++17string-view

解决方案


以这种方式返回 string_view 是不安全的(或 UB),还是我可以良心继续这样做

是的。你使用它的方式完全没问题。您的函数string_view返回的toString数据形成了一个数据视图,该视图将保持不变,直到程序终止。

或者,是否有更好(更快/更安全)的方法来解决这个枚举到字符串的一般问题?

您可以在其中创建一个constexpr带有switch-state 的函数,如下所示:

constexpr std::string_view toString(Color color)
{
    switch (color) {
    case Color::red:   return "red";
    case Color::green: return "green";
    ...
    }
}

如果在编译时评估函数,效率应该没有差异。但是编译器可以检查您case是否为所有可能的 s 添加了 -statements Color,如果没有,它会发出警告。也不需要Color::last这种方式。

保持 theenumstd::arrayorswitch语句同步可能很烦人,尤其是当您有很多枚举值时。X 宏在这里可能会有所帮助。


推荐阅读