Читать «Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14» онлайн - страница 23
Скотт Мейерс
template<typename Container, typename Index> // С++14; работает,
decltype(auto) // но все еще
authAndAccess(Containers с, Index i) // требует
{ // уточнения
authenticateUser();
return c[i];
}
Теперь authAndAccess
действительно возвращает то же, что и c[i]
. В частности, в распространенном случае, когда c[i]
возвращает Т&
, authAndAccess
также возвращает Т&
, и в том редком случае, когда c[i]
возвращает объект, authAndAccess
также возвращает объект.
Использование decltype(auto)
не ограничивается возвращаемыми типами функций. Это также может быть удобно для объявления переменных, когда вы хотите применять правила вывода типа decltype
к инициализирующему выражению:
Widget w;
const Widget& cw = w;
auto myWidget1 = cw; // Вывод типа auto:
// тип myWidget1 - Widget
decltype(auto) myWidget2 = cw; // Вывод типа decltype:
// тип myWidget2 - const Widget&
Я знаю, что вас беспокоят два момента. Один из них — упомянутое выше, но пока не описанное уточнение authAndAccess
. Давайте, наконец-то, разберемся в этом вопросе. Еще раз посмотрим на версию authAndAccess
в С++14:
template<typename Container, typename Index>
decltype(auto) authAndAccess(Container& с, Index i);
Контейнер передается как lvalue-ссылка на неконстантный объект, поскольку возвращаемая ссылка на элемент контейнера позволяет клиенту модифицировать этот контейнер. Но это означает, что этой функции невозможно передавать контейнеры, являющиеся rvalue. rvalue невозможно связать с lvalue-ссылками (если только они не являются lvalue-ссылками на константные объекты, что в данном случае очевидным образом не выполняется).
Надо сказать, что передача контейнера, являющегося rvalue, в authAndAccess
является крайним случаем. Такой rvalue-контейнер, будучи временным объектом, обычно уничтожается в конце инструкции, содержащей вызов authAndAccess
, а это означает, что ссылка на элемент в таком контейнере (то, что должна вернуть функция authAndAccess
) окажется “висячей” в конце создавшей ее инструкции. Тем не менее передача временного объекта функции authAndAccess
может иметь смысл. Например, клиент может просто хотеть сделать копию элемента во временном контейнере:
std::deque<std::string> makeStringDeque(); // Фабричная функция
// Делаем копию пятого элемента deque, возвращаемого
// функцией makeStringDeque
auto s = authAndAccess(makeStringDeque(), 5);
Поддержка такого использования означает, что мы должны пересмотреть объявление функции authAndAccess
, которая должна принимать как lvalue, так и rvalue. Можно использовать перегрузку (одна функция объявлена с параметром, представляющим собой lvalue-ссылку, а вторая — с параметром, представляющим собой rvalue-ссылку), но тогда нам придется поддерживать две функции. Избежать этого можно, если у нас будет функция authAndAccess
, использующая ссылочный параметр, который может быть связан как с lvalue, authAndAccess
может быть объявлена следующим образом: