Функция принудительного завершения времени жизни локальной переменной
Кратко
Предлагается добавить в стандарт функцию, которая бы позволяла принудительно завершать время жизни локальной переменной, делая её недоступной для дальнейшего использования. Также предлагается реализовать copy elision (или его аналог) для xvalues вида const T&&.
Проблема
На данный момент в C++ нет возможности убрать из скоупа переменную. В лучше случае, мы можем сделать std::move, который оставит после себя обнулённый (в зависимости от определения) объект.
Пример:
{
std::shared_ptr< Some > local_variable = std::make_shared< Some >(...);
...
some_function(std::move(local_variable));
...
local_variable->some_method(); // runtime (!) error
}
Предложение
Добавить функцию - пусть это будет, например, std::drop, - которая бы возвращала по аналогии с std::move xvalue expression, но при этом бы гарантировала ошибку компиляции в случае повтороного доступа к переменной. Тогда пример выше можно было бы написать следующим образом:
{
/* [const] */ std::shared_ptr< Some > local_variable = std::make_shared< Some >(...);
...
some_function(std::drop(local_variable)); // тут переменная "перемещается в функцию" `some_function`, но удаляется из локального скоупа
...
// local_variable->some_method(); // _compile_ time (!) error: "local_variable was dropped before at line ..."
// auto local_variable{}; // _compile_ time (!) error - переопределение должно быть запрещено
// decltype(local_variable) new_variable{}; // _compile_ time (!) error - такое тоже, вероятно, стоит запретить
}
Если сделать переменную local_variable константной, то std::move теряет свой смысл (константная переменная должна "обнулиться", что явно не самый ожидаемый результат), а вот std::drop остаётся актуальной - нет смысла заботиться о константности переменной, удалённой из скоупа. То есть функция std::drop должна либо преобразовывать const T&& в T&&, либо реализовать copy elision. Последний вариант кажется более удачным.
Ещё один пример для дискуссии:
{
std::shared_ptr< Some > local_variable = std::make_shared< Some >(...);
...
// использование std::drop внутри условных операторов тоже стоит запретить. Как и внутри `switch .. case` и `try ... catch`.
/*
if (is_drop) {
std::drop(local_variable);
}
*/
// Использование функции вместе с её аргументом в тех случаях, когда порядок вычисления не определён, не допускается
// some_function(std::drop(local_variable), local_variable);
...
// а вот такое, возможно, стоит разрешить
if constexpr (constexpr_is_drop) {
std::drop(local_variable);
}
}
Ещё пример. Рассмотрим следующую структуру:
struct Message {
const std::string _text;
Message() = delete;
Message(const Message&) = delete;
Message(Message&&) = delete;
Message(const Message&&);
Message operator = (Message&&) = delete;
Message operator = (const Message&&);
static std::unique_ptr< Message > make();
};
Тут всё просто: сообщение, которое:
а) Константно. Если мы создали сообщение (например, с помощью статического метода класса Message), то оно не должно меняться.
б) Существует в единственном экземпляре, для чего мы запретили конструктор копирования.
в) Не может быть пустым, для чего мы запретили конструктор и оператор перемещения и конструктор по умолчанию.
Пусть мы хотим после этого определить следующий тип:
struct Package {
const Message _message;
const int _id;
Package(const Message&& message, int id)
: _message(std::forward< const Message >(message) // копирование, а не перемещение
// : _message(message) // ошибка компиляции (конструктор копирования `Message` удалён)
// : _message(std::forward< Message >(message) // ошибка компиляции (_message константна)
, _id(id)
{}
};
int main() {
const Message message = ...;
...
const Package package(std::move(message), 3); // копирование
// const Package package(std::drop(message), 3); // перемещение
}
Ошибки компиляции логичны, иначе нарушались бы гарантии константности. Но с функцией std::drop можно было бы реализовать перемещение констант.
Предложение суммарно:
- Добавить функцию, позволяющую удалять локальные переменные из скоупа.
- Реализовать copy elision (или его аналог) для xvalues вида
const T&&(с учётом случаев первого пункта).
Плюсы:
- Появляется возможность сделать код более понятным и безопасным
- Расширяются возможности при работе с константами
- Добавляются подсказки для компиляторов/статических анализаторов
Минусы:
- Возможно, чтобы разделить временные объекты и перемещаемые константы, придётся добавлять новый тип выражений (?) ? ...
Обратная совместимость.
Реализация функции std::drop не нарушает обратную совместимость. Расширение copy elision для xvalues вида const T&& может нарушить обратную совместимость, если не будет привязано к использованию функции std::drop.