#опытным
В прошлом посте мы видели, что разница в использовании decltype(auto) и auto&& при выводе типа возвращаемого значения функции проявляется только при возврате объектов, на которые явно не навесили ссылки. Иногда мы можем это отследить глазами и потенциально использовать более простую версию вывода типа. Однако не все мы можем отследить глазами.
Доступ к элементам std::deque всегда возвращает честную левую ссылку. И это почти полностью справедливо для std::vector, кроме одного исключения.
Это std::vector<bool>. Эта специализация вектора возвращает не честную ссылку на объект типа bool, в временный proxy объект. Дело в том, что тип bool занимает как минимум 1 байт, так как это минимально адресуемая ячейка памяти. Но логически он хранит всего 1 бит информации. Если бы мы могли как-то по-хитрому хранить булевы значения, чтобы каждое из них занимало всего 1 бит, то мы бы уменьшили потребление памяти как минимум в 8 раз! Именно это и делается в специализации для bool. Там булевы значения хранятся в виде битов более вместительного типа(int), а для получения доступа к значениям используется proxy объект reference, который неявно приводится bool.
Посмотрим, к чему приводит эта маленькая особенность:
template<typename Container, typename Index>
auto&& processAndAccess(Container& c, Index i) {
// do something
// ...
return c[i];
}
std::vector<int> v = {1, 2, 3};
// OK - returns int&
processAndAccess(v, 1) = 3;
std::vector<bool> v2 = {true, false, false};
// NOT OK - returns vector<bool>::reference&& which is a dangling reference
processAndAccess(v2, 1) = true;
Для инстанциаций вектора с любыми другими типами все хорошо работает. А для bool специализации мы получаем висячую ссылку и UB.
При использовании decltype(auto) таких проблем нет. Можете поиграться с примерами на cppinsights и godbolt.
Proxy объекты не так часто используются. Один из основных кейсов - это доступ к элементам многомерных структур. Однако при проектировании и написании кода, которым будут пользоваться другие люди, нужно учитывать такие вещи и писать в первую очередь безопасный код.
Спасибо @thonease за предоставления исходного кода)
По итогу серии постов: использование auto&& безопасно при выводе типа локального объекта, но небезопасно при выводе типа возвращаемого значения функции. В последнем случае нужно использовать decltype(auto).
Be safe. Stay cool.
#cppcore #template