Разделение строки во время компиляции дает разные результаты на разных компиляторах - PullRequest
0 голосов
/ 07 февраля 2019

Я пытаюсь разбить строку во время компиляции.Я определил функцию 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

Я добавил вторую функцию разделения, идентичную оригинальной, за исключением того, что она печатает промежуточную подстроку внутри разделенияфункция.И 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;
}

Edit 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

Здесь является ссылкой наобновленный полный код.

1 Ответ

0 голосов
/ 07 февраля 2019

На самом деле это ошибка компилятора.Я не знаю, что именно отвечает за ошибку, но она находится внутри 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;
}

Edit

Получаетсяmsvc очень плох в оценке константных выражений по сравнению с gcc и clang.Я нашел много других сценариев (вокруг std::string_view), которые также не компилируются.

...