从函数返回 string_view
returning string_view from function
我正在编写很多 string_view 擅长的解析器代码,并且很喜欢这种类型。我最近阅读了 ArthurO'Dwyer 的文章 std::string_view is a borrow type,他在文章中得出结论认为 string_view(和其他 'borrow types')可以很好地使用,只要它们“......仅作为函数参数出现并且用于-循环控制变量。” (有几个例外)。
但是,我最近开始使用 string_view 作为将枚举转换为字符串(我经常使用)的函数的 return 值,像这样 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;
}
我这样做的原因是:
- 通过将其作为 string_view 存储在数组中,我可以使整个 table constexpr.
- 通过直接 returning string_view,不需要转换字符串表示形式,因此整个函数可以是 constexpr,或者至少避免创建不必要的字符串,即使使用非 constexpr 参数。
- 使用 table constexpr 的一个副作用是我可以使用 static_assert 来检查枚举的所有元素是否都在 table 中,这对于捕捉来说真的很棒添加到枚举。我真的不喜欢必须将 'last' 枚举值放在那里,但我没有看到更好的解决方案。
所以我的问题真的是,return这样 string_view 是不安全的(或 UB ) 以任何方式,或者我可以凭良心继续这样做吗?
或者,是否有更好的 (faster/safer) 方法来解决这个枚举到字符串的一般问题?
Addition:在阅读了 G. Sliepen 的非常好的答案之后,我想补充一下我对他的回答的评论:我通常也有相反的功能,例如:
constexpr Color fromString(string_view str)
{
// No-match handling omitted
return std::ranges::find(colorNames, color, &cts::second)->first;
}
在那些情况下,我确实需要将翻译作为单独的 table 以便两个函数都可以使用它。但在很多其他情况下,包含 switch 语句的函数是最简单和最好的。
is returning the string_view this way unsafe (or UB) in any way, or can I keep on doing this with good conscience?
是的。你使用它的方式是完全可以的。 toString
函数返回的 string_view
形成了一个数据视图,该视图在程序终止之前将保持不变。
Alternatively, is there a better (faster/safer) way of solving this general problem of enum-to-string?
您可以创建一个 constexpr
函数,其中包含 switch
语句,如下所示:
constexpr std::string_view toString(Color color)
{
switch (color) {
case Color::red: return "red";
case Color::green: return "green";
...
}
}
如果在编译时计算函数,效率应该没有差异。但是编译器可以检查您是否为所有可能的 Color
添加了 case
语句,如果没有,它会发出警告。这样也不需要 Color::last
。
保持 enum
和 std::array
或 switch
语句同步可能很烦人,尤其是当您有很多枚举值时。 X macros 在这里可能会有帮助。
我正在编写很多 string_view 擅长的解析器代码,并且很喜欢这种类型。我最近阅读了 ArthurO'Dwyer 的文章 std::string_view is a borrow type,他在文章中得出结论认为 string_view(和其他 'borrow types')可以很好地使用,只要它们“......仅作为函数参数出现并且用于-循环控制变量。” (有几个例外)。
但是,我最近开始使用 string_view 作为将枚举转换为字符串(我经常使用)的函数的 return 值,像这样 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;
}
我这样做的原因是:
- 通过将其作为 string_view 存储在数组中,我可以使整个 table constexpr.
- 通过直接 returning string_view,不需要转换字符串表示形式,因此整个函数可以是 constexpr,或者至少避免创建不必要的字符串,即使使用非 constexpr 参数。
- 使用 table constexpr 的一个副作用是我可以使用 static_assert 来检查枚举的所有元素是否都在 table 中,这对于捕捉来说真的很棒添加到枚举。我真的不喜欢必须将 'last' 枚举值放在那里,但我没有看到更好的解决方案。
所以我的问题真的是,return这样 string_view 是不安全的(或 UB ) 以任何方式,或者我可以凭良心继续这样做吗?
或者,是否有更好的 (faster/safer) 方法来解决这个枚举到字符串的一般问题?
Addition:在阅读了 G. Sliepen 的非常好的答案之后,我想补充一下我对他的回答的评论:我通常也有相反的功能,例如:
constexpr Color fromString(string_view str)
{
// No-match handling omitted
return std::ranges::find(colorNames, color, &cts::second)->first;
}
在那些情况下,我确实需要将翻译作为单独的 table 以便两个函数都可以使用它。但在很多其他情况下,包含 switch 语句的函数是最简单和最好的。
is returning the string_view this way unsafe (or UB) in any way, or can I keep on doing this with good conscience?
是的。你使用它的方式是完全可以的。 toString
函数返回的 string_view
形成了一个数据视图,该视图在程序终止之前将保持不变。
Alternatively, is there a better (faster/safer) way of solving this general problem of enum-to-string?
您可以创建一个 constexpr
函数,其中包含 switch
语句,如下所示:
constexpr std::string_view toString(Color color)
{
switch (color) {
case Color::red: return "red";
case Color::green: return "green";
...
}
}
如果在编译时计算函数,效率应该没有差异。但是编译器可以检查您是否为所有可能的 Color
添加了 case
语句,如果没有,它会发出警告。这样也不需要 Color::last
。
保持 enum
和 std::array
或 switch
语句同步可能很烦人,尤其是当您有很多枚举值时。 X macros 在这里可能会有帮助。