繁体   English   中英

在编译时拆分字符串会在不同的编译器上产生不同的结果

[英]Splitting a string at compile time gives different results on different compilers

我正在尝试在编译时拆分一个字符串。 我定义了一个像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函数内的中间子字符串。 在这种情况下,msvc和gcc都打印相同,这是上面的预期结果。

为什么结果不同? 我在某处调用了UB吗?

完整的代码可以在这里找到

编辑

看起来这是msvc中的一个错误。 在运行时调用该函数会产生预期的结果:

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运行常量表达式的解释器时,实际上存在一个错误。 我添加了另一个函数来访问函数外部的变量:

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

是更新的完整代码的链接。

它实际上是一个编译器错误。 我不知道究竟是什么导致了这个bug,但它在std::string_view::find_first_of 奇怪的是,这个错误只发生在持续评估期间(编译时)。 据我所知,此函数的运行时行为与预期一致。

这是split的工作实现:

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 = std::string_view::npos;
        for (std::size_t j = start; j < str.length(); j++)
        {
            if (str[j] == ',')
            {
                end = j;
                break;
            }
        }

        arr[i] = str.substr(start, end - start);        
        start = end + 1;
    }

    return arr;
}

编辑

事实证明,与gcc和clang相比,msvc在评估常量表达式方面非常糟糕。 我发现许多其他场景(所有围绕std::string_view )也无法编译。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM