У меня есть следующее объявление -
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
}
Я хочу понять причину такого поведения компилятора.
А как насчет constexpr std::string get_compilation_year() { return {__DATE__[7], __DATE__[8], __DATE__[9], __DATE__[10]}; } вместо этого? Можно было бы даже return {__DATE__ + 7, __DATE__ + 7 + 4};, но clang это не нравится.
Я сомневаюсь, что это минимальный воспроизводимый образец.
static constexpr std::string_view compilation_year{&(__DATE__)[0] + 7, 4}; сделаю





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()};
Спасибо за этот ответ, но я до сих пор не понимаю, почему добавление пустой строки "" заставляет его компилироваться и работать правильно.
@ChrisNikolaev Мое единственное предположение: использование оператора + каким-то образом обманывает MSVC, так что он не понимает, что происходит. Пробовали ли вы инициализировать переменную constexpr с помощью этой функции? В моей версии MSVC это не работает.
Да, в другом исходном файле у меня есть: auto copyright_text = "Copyright (C) XXXXX 1986 - " + get_compilation_year();
@ChrisNikolaev Попробуйте сделать это constexpr
Ты прав. Добавление constexpr не работает. По сути, он компилируется, потому что MSVC обманут, и ничто в стандарте C++ не объясняет это странное поведение, при котором объединение значения и пустой строки имеет какое-либо значение. Спасибо!
@ChrisNikolaev Всегда пожалуйста! Да, по крайней мере, я считаю, что именно это происходит внутри MSVC. Остальные компиляторы отказываются как от версии + "", так и от версии без нее.
std::to_string(int)— это неconstexpr, поэтому даже если он скомпилируется, я был бы удивлён, если бы вы могли действительно инициализировать переменнуюconstexpr, вызвавget_compilation_year().