我试图在编译时分割一个字符串。我定义了一个函数
split
喜欢
#include <array>
#include <string_view>
template <std::size_t N>
constexpr std::array<std::string_view, N> split(std::string_view str)
{
std::array<std::string_view, N> arr{};
std::size_t start = 0, end = 0;
for (std::size_t i = 0; i < N && end != std::string_view::npos; i++)
{
end = str.find_first_of(',', start);
arr[i] = str.substr(start, end - start);
start = end + 1;
}
return arr;
}
使用方法如下:
constexpr std::string_view str = "one,two,three,four,five";
constexpr std::array<std::string_view, 5> arr = split<5>(str);
msvc和gcc都可以编译。但是clang已经拒绝了这条代码
std::string_view::find_first_of
不会导致常量表达式(这是编译器错误吗?).
当我像这样测试结果时:
int main()
{
std::cout << str << "\n\n";
for (auto i = 0; i < arr.size(); i++)
std::cout << arr[i] << "\n";
return 0;
}
msvc打印
one,two,three,four,five
one
two
thr
e,f
ur,
而gcc给了我预期的结果
one,two,three,four,five
one
two
three
four
five
我添加了第二个split函数,它与原始函数相同,只是它打印出split函数中的中间子字符串。在这种情况下,msvc和gcc都会打印相同的结果,这是上面的预期结果。
为什么结果不同?我有没有在什么地方调用UB?
完整的代码可以找到
here
编辑
看起来这是msvc中的一个bug。在运行时调用函数会产生预期的结果:
int main()
{
std::cout << str << "\n\n";
for (auto i = 0; i < arr.size(); i++)
std::cout << arr[i] << "\n";
auto arr2 = split<5>(str);
for (auto i = 0; i < arr2.size(); i++)
std::cout << arr2[i] << "\n";
return 0;
}
编辑2
当msvc为常量表达式运行解释器时,看起来它内部实际上有一个bug。我添加了另一个函数来访问函数之外的变量:
constexpr decltype(split<5>(str)) arr = split<5>(str);
constexpr decltype(split_sizes<5>(str)) arr_sizes = split_sizes<5>(str);
template <std::size_t N>
constexpr std::array<std::array<std::size_t, 3>, N> split_sizes(std::string_view str)
{
std::array<std::array<std::size_t, 3>, N> arr{};
std::size_t start = 0, end = 0;
for (std::size_t i = 0; i < N && end != std::string_view::npos; i++)
{
end = str.find_first_of(',', start);
auto sub = str.substr(start, end - start);
arr[i] = { sub.length(), start, end };
start = end + 1;
}
return arr;
}
int main()
{
for (auto i = 0; i < arr.size(); i++)
std::cout << arr[i] << "\tlen=" << arr_sizes[i][0] << " start=" << arr_sizes[i][1] << " end=" << arr_sizes[i][2] << "\n";
std::cout << "\n";
auto arr2 = split<5>(str);
auto arr_sizes2 = split_sizes<5>(str);
for (auto i = 0; i < arr2.size(); i++)
std::cout << arr2[i] << "\tlen=" << arr_sizes2[i][0] << " start=" << arr_sizes2[i][1] << " end=" << arr_sizes2[i][2] << "\n";
return 0;
}
在msvc上给出以下结果:
one,two,three,four,five
one len=3 start=0 end=3
two len=3 start=4 end=7
thr len=3 start=8 end=11
e,f len=3 start=12 end=15
ur, len=3 start=16 end=19
one len=3 start=0 end=3
two len=3 start=4 end=7
three len=5 start=8 end=13
four len=4 start=14 end=18
five len=4 start=19 end=18446744073709551615
Here
是指向更新的完整代码的链接。