Страницы

Поиск по вопросам

четверг, 1 ноября 2018 г.

Разъясните смысл std::in_place, std::in_place_type, std::in_place_index для std::optional, std::variant, и std::any

С какой целью они используются для std::optional, std::variant, and std::any? Каков принцип их работы. Если можно, с примерами. Спасибо.


Ответ

Эти переменные-теги и соответствующие им типы предназначены для решения следующей задачи.
Пусть у меня есть
std::variant v;
и я решил инициализировать эту переменную как
std::variant v("Hello World");
Правила инициализации std::variant совпадают с правилами overload resolution (для некоей воображаемой функции). Эти правила говорят, что я в этом случае получу вариант, хранящий const char *. Но вдруг мне по какой-то причине нужно, чтобы литерал "Hello World" использовался именно как инициализатор для варианта, хранящего std::string
Вот для достижения этой цели я и могу использовать std::in_place_type с шаблонным аргументом соответствующего типа
std::variant v(std::in_place_type, "Hello World");
std::in_place_type в этом случае - это "фиктивный" тег-аргумент, задача которого - просто заставить компилятор выбрать правильный конструктор в процессе разрешения перегрузок (overload resolution). Больше этот аргумент ничего не делает.
std::in_place_index делает то же самое, только выбирая тип по порядковому индексу в списке шаблонных параметров std::variant
std::variant v(std::in_place_index<0>, "Hello World");
Пользуясь этим тегами я могу делать forwarding аргументов в любой конструктор std::string, в том числе многоаргументный
std::variant v(std::in_place_type, 10, 'a'); // Создает внутренний `std::string(10, 'a')`
Вы можете заметить, что того же эффекта я мог достичь, вручную сначала сконструировав временный объект
std::variant v(std::string("Hello World"));
но эта версия действительно сначала создает временный объект std::string("Hello World"), а затем перемещает его во внутренний объект std::variant. Это не всегда то, что нужно. Идея с std::in_place_type (и компанией) заключается в том, чтобы получить forwarding передаваемых аргументов прямо в конструктор создаваемого внутреннего объекта, без формирования каких-либо промежуточных временных объектов.

std::in_place_type также применим с std::any для явного указания типа хранимого объекта и c той же целью получения forwarding: сконструировать внутренний объект сразу, через forwarding аргументов, без промежуточного перемещения.

std::in_place предназначен для использования с std::optional и тоже вызывает именно forwarding последующих аргументов прямо во внутренний конструируемый объект. В std::optional выбирать тип не нужно, ибо он зафиксирован заранее, поэтому std::in_place не имеет никаких шаблонных параметров, т.е. является обычной переменной, а не шаблоном переменной.

Комментариев нет:

Отправить комментарий