【问题标题】:returning string_view from function从函数返回 string_view
【发布时间】:2021-12-09 15:33:53
【问题描述】:

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

但是,我最近开始使用 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++17 string-view


    【解决方案1】:

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

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

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

    您可以创建一个 constexpr 函数,其中包含 switch 语句,如下所示:

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

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

    enumstd::arrayswitch 语句保持同步可能很烦人,尤其是在您有大量枚举值的情况下。 X macros 在这里可能会有所帮助。

    【讨论】:

    • 是的,这是一个不错的选择。我在原始帖子中没有提到它,但是在我的代码中, enum-string_view 数组也经常以其他方式使用,例如在像Color fromString(string_view str) 这样的函数中,它匹配对的第二个元素以返回第一个。为了处理这个问题,我需要数组,而不仅仅是函数中的 switch 语句。但否则你是对的,一个简单的 switch 函数(仍然返回 string_view)有时可能会更好。
    • 您仍然可以使用if-else 语句链而不是switch 来处理fromString()。但是如果不能在编译时进行评估(因为str 来自一些外部输入),如果你有大量颜色,最好使用std::unordered_map 之类的东西而不是std::array
    • 但是如果在 fromString 函数中使用 if-else (或类似的),我突然需要在 toStringfromString 函数中复制 enum-string_view 信息 -这是我永远不会做的事情。
    • 当然,尽管 X 宏也可以提供帮助。您可以只使用一个 std::array 将枚举值映射到字符串,但在运行时搜索它可能会很慢。另一种选择是使用Boost::Bimap 之类的东西。或者也许这个Magic Enum 库是你可以使用的。也许使用 C++23,我们将获得对 reflection 的语言支持。
    • 我想 X 宏可能会有所帮助,但我们公司有一个非常严格的“无宏”标准(我支持)。我知道通过反思可以打开的可能性,并且真的,真的希望我能在几年内使用它。不过,另外两个看起来很有趣,谢谢:-)
    猜你喜欢
    • 2021-06-17
    • 1970-01-01
    • 1970-01-01
    • 2013-05-15
    • 1970-01-01
    • 2016-08-28
    • 2015-01-26
    • 2018-05-19
    • 2014-12-05
    相关资源
    最近更新 更多