«constexpr» и std::to_string в C++ 20

У меня есть следующее объявление -

constexpr unsigned int compileYear = (__DATE__[7] - '0') * 1000 + (__DATE__[8] - '0') * 100 + (__DATE__[9] - '0') * 10 + (__DATE__[10] - '0');

При использовании Visual Studio 2022 и C++ 20 эта функция не может скомпилироваться:

constexpr std::string get_compilation_year()
{
    return std::to_string(compileYear); // cannot result in a constant expression 
}

И по какой-то причине добавление к нему пустой строки приводит к успешной компиляции:

constexpr std::string get_compilation_year()
{
    return std::to_string(compileYear) + ""; // OK, it works 
}

Я хочу понять причину такого поведения компилятора.

std::to_string(int) — это не constexpr, поэтому даже если он скомпилируется, я был бы удивлён, если бы вы могли действительно инициализировать переменную constexpr, вызвав get_compilation_year().
Ted Lyngmo 20.05.2024 19:27

А как насчет constexpr std::string get_compilation_year() { return {__DATE__[7], __DATE__[8], __DATE__[9], __DATE__[10]}; } вместо этого? Можно было бы даже return {__DATE__ + 7, __DATE__ + 7 + 4};, но clang это не нравится.

Ted Lyngmo 20.05.2024 19:33

Я сомневаюсь, что это минимальный воспроизводимый образец.

Red.Wave 20.05.2024 19:35
static constexpr std::string_view compilation_year{&(__DATE__)[0] + 7, 4}; сделаю
Pepijn Kramer 20.05.2024 19:55
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
125
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

std::to_string(int) не constexpr, поэтому вы не сможете инициализировать переменную constexpr с помощью вызова get_compilation_year(), даже если она компилируется в своем текущем состоянии.

Вместо этого вы можете использовать один из конструкторов constexprstd::string:

template< class InputIt >
constexpr std::string(InputIt first, InputIt last,
                      const Allocator& alloc = Allocator() );

или

constexpr std::string(std::initializer_list<char> ilist,
                      const Allocator& alloc = Allocator() );

В настоящее время в MSVC, gcc и clang в этом контексте работает только тот, который принимает std::initializer_list<char>. Clang не нравится версия итератора (пока).

Итак, (несколько) портативная версия C++20 может выглядеть так:

constexpr std::string get_compilation_year() {
    return {__DATE__[7], __DATE__[8], __DATE__[9], __DATE__[10]};
}

Или, как предложил Пепейн Крамер, вместо этого используйте std::string_view:

constexpr std::string_view get_compilation_year() {
    return {__DATE__ + 7, 4};
}

Несколько переносимая версия работает до тех пор, пока конструктор не выделяет ресурсы. Для более длинного текста, включая текущий год, вы можете инициализировать std::array и создать над ним std::string_view:

#include <array>
#include <string_view>
#include <utility>

constinit auto copyright_data = [] {
    auto& beg = "Copyright (C) XXXXX 1986 - ";
    return [&]<std::size_t... I>(std::index_sequence<I...>) {
        return std::array{beg[I]..., __DATE__[7], __DATE__[8], __DATE__[9],
                          __DATE__[10]};
    }(std::make_index_sequence<sizeof(beg) - 1>{});
}();

constexpr std::string_view copyright_text{copyright_data.begin(),
                                          copyright_data.end()};

Демо

Спасибо за этот ответ, но я до сих пор не понимаю, почему добавление пустой строки "" заставляет его компилироваться и работать правильно.

Chris Nikolaev 20.05.2024 20:01

@ChrisNikolaev Мое единственное предположение: использование оператора + каким-то образом обманывает MSVC, так что он не понимает, что происходит. Пробовали ли вы инициализировать переменную constexpr с помощью этой функции? В моей версии MSVC это не работает.

Ted Lyngmo 20.05.2024 20:03

Да, в другом исходном файле у меня есть: auto copyright_text = "Copyright (C) XXXXX 1986 - " + get_compilation_year();

Chris Nikolaev 20.05.2024 20:04

@ChrisNikolaev Попробуйте сделать это constexpr

Ted Lyngmo 20.05.2024 20:05

Ты прав. Добавление constexpr не работает. По сути, он компилируется, потому что MSVC обманут, и ничто в стандарте C++ не объясняет это странное поведение, при котором объединение значения и пустой строки имеет какое-либо значение. Спасибо!

Chris Nikolaev 20.05.2024 20:08

@ChrisNikolaev Всегда пожалуйста! Да, по крайней мере, я считаю, что именно это происходит внутри MSVC. Остальные компиляторы отказываются как от версии + "", так и от версии без нее.

Ted Lyngmo 20.05.2024 20:10

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