Всегда ли операции со строковым литералом должны быть constexpr?

Посмотрите этот код на Godbolt. Он содержит простую функцию, которая преобразует строку в целое число, игнорируя все нецифровые символы. Он также поддерживает анализ отрицательных целых чисел, если целочисленный тип подписан.

Поскольку функция constexprи вызывается со строковым литералом, GCC всегда оценивает функцию во время компиляции и выдает только одну инструкцию сборки. MSVC никогда этого не делает и всегда генерирует множество инструкций для обработки строкового литерала.

Однако clang выглядит странно - для целых чисел со знаком, если строковый литерал содержит 5 символов или меньше, функция вычисляется во время компиляции, в противном случае создается много ассемблерного кода. Для целых чисел без знака порог составляет 10 символов. Но если я заверну буквально вstd::string_view, при clang он всегда вычисляется во время компиляции, независимо от длины строкового литерала.

Итак, какой компилятор "правильный"? Требует ли стандарт C++ обработки строковых литералов во время компиляции? Или это зависит от конкретного компилятора?

И почему clang генерирует код времени выполнения для определенного более длинного строкового литерала, в то время как он оценивает функцию во время компиляции для того же литерала, если он заключен в std::string_view? я знаю этоstd::string_view имеет constexpr конструктор, что делает его еще более странным (прямая обработка во время компиляции невозможна, но вложенное построение временного объекта и его обработка во время компиляции в порядке - это похоже на большую работу для компилятора, чем в первом случае).

Для удобства вот функция по ссылке выше:

template< typename T, typename StringView >
constexpr T stringToInt( StringView && str ) noexcept requires std::is_integral_v< T >
{
    T ret{ 0 };
    auto isNegative = [ & ]() noexcept
    {
        if constexpr( std::is_signed_v< T > )
        {
            for ( auto && c : str )
            {
                if ( c == '-' ) return true;
                if ( ( c >= '0' ) && ( c <= '9' ) ) return false;
            }
            return false;
        }
        else
            return false;
    }();
    for ( auto && c : str )
    {
        if ( ( c >= '0' ) && ( c <= '9' ) )
        {
            ret = ret * 10 + c - '0';
        }
    }
    return isNegative ? -ret : ret;
}

А некоторые вызывают примеры с комментариями, которые создает каждый компилятор:

return stringToInt< int >( "-5123" ); // single instruction on both GCC and Clang
return stringToInt< int >( "-51234" ); // single instruction on GCC, lots of instructions on clang
return stringToInt< int >( std::string_view{ "-51234" } ); // single instruction on both GCC and Clang
// MSVC in all those cases generates a lot of instructions

0 ответов

Другие вопросы по тегам