В функции разбирают именованный объект через структурированное связывание: какой выбор объявления сохранит ссылки на его элементы вместо создания копии объекта?
Для именованного объекта нужно использовать структурированное связывание с auto&. Оно связывает скрытый объект с исходным объектом по ссылке, поэтому имена связывания обращаются к его исходным элементам. Вариант с auto создаёт отдельный объект-инициализатор, а не работает с исходным объектом напрямую.
Структурированные связывания появились в C++17, чтобы кратко извлекать несколько частей объекта без ручного обращения к полям или вызовов std::get. Они особенно полезны для пар, кортежей, результатов алгоритмов и простых агрегатов.
Механизм поддерживает массивы, типы с доступными нестатическими полями и так называемые tuple-like-типы, для которых определены std::tuple_size, std::tuple_element и подходящий get.
Запись с auto может незаметно скопировать именованный объект. Это опасно для крупных структур, объектов с дорогим копированием или типов, чьё изменение ожидается через распакованные имена.
При ошибочном выборе квалификатора программа может либо напрасно копировать данные, либо изменять исходный объект там, где ожидалась независимая копия. Поэтому важно различать тип скрытого объекта, создаваемого объявлением связывания, и типы самих имён связывания.
В объявлении структурированного связывания компилятор логически вводит скрытый объект, который инициализируется правой частью. Имена связывания обращаются к элементам этого скрытого объекта, а не являются обычными независимыми переменными.
При auto [x, y] скрытый объект обычно создаётся как отдельный объект и копируется из именованного исходного объекта. Поэтому x и y обозначают элементы копии.
При auto& [x, y] скрытый объект является ссылкой на исходный объект. Имена x и y обращаются к его элементам, а изменения через них изменяют исходный объект.
Если исходный объект объявлен как const, то через auto& нельзя получить изменяемые элементы: связывание сохранит константность. Для константной ссылки обычно используют const auto&.
auto&& применяют, когда нужно сохранить категорию значения и корректно работать как с lvalue, так и с rvalue, но этот вариант требует отдельного анализа времени жизни объекта. Для обычного именованного объекта, который нужно изменять без копирования, предпочтителен явный auto&.
Функция получает большую структуру конфигурации и извлекает из неё несколько параметров. Вариант с auto [name, timeout] создаёт копию всей структуры, если правая часть является именованным объектом. Это увеличивает затраты и может скрыть ошибку, когда изменения через name или timeout должны были менять конфигурацию.
Можно использовать auto& [name, timeout], если функция должна изменять исходную конфигурацию. Плюс такого решения — отсутствие копирования и явная передача намерения; минус — изменения становятся видимыми для вызывающего кода.
Если функция только читает данные, разумнее применить const auto& [name, timeout]. Это избегает копирования и одновременно запрещает изменение исходного объекта, поэтому в данной ситуации такой вариант является наиболее безопасным.
auto [a, b] отдельные копии каждого элемента?Нет. Сначала создаётся скрытый объект, инициализированный исходным значением, а имена связывания обращаются к его подобъектам. Поэтому корректнее говорить о копировании объекта-инициализатора, а не о независимом создании двух переменных для каждого элемента.
auto&& с именованным объектом?Для именованного объекта выражение является lvalue, поэтому скрытая переменная связывается с ним как ссылка на lvalue, и изменения через связывание затрагивают исходный объект. С временным объектом правила сложнее: время его жизни может быть продлено связыванием, но безопасность зависит от конкретного выражения и того, не возвращаются ли ссылки за пределы допустимого времени жизни.
auto&& не является универсальным способом безопасно распаковывать любые значения. Его следует выбирать только при осознанной работе с категориями значений и временем жизни.
const auto [a, b] не делает исходный объект константным?Потому что при таком объявлении с именованным объектом создаётся отдельный скрытый объект, имеющий const-квалификацию. Константными становятся элементы копии, а исходный объект не меняется и не становится const.
Чтобы связаться именно с исходным объектом и запретить его изменение через имена, нужно использовать const auto& [a, b]. Здесь скрытый объект является константной ссылкой на исходный объект, поэтому копирования нет, а доступ к элементам только для чтения.