Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14 - страница 18

стр.

>template // Окончательная

>auto                                         // версия для

>authAndAccess(Container&& с, Index i)        // C++11

> -> decltype(std::forward(с)[i]) {

> authenticateUser();

> return std::forward(с)[i]);

>}

Вторым беспокоящим моментом является мое замечание в начале этого раздела о том, что >decltypeпочти всегда дает тип, который вы ожидаете, т.е. что он редко преподносит сюрпризы. По правде говоря, вряд ли вы столкнетесь с этими исключениями из правила, если только вы не занимаетесь круглосуточно написанием библиотек.

Чтобы полностью понимать поведение >decltype, вы должны познакомиться с некоторыми особыми случаями. Большинство из них слишком невразумительны, чтобы быть размещенными в этой книге, но один из них приводит к лучшему пониманию >decltype и его применения.

Применение >decltype к имени дает объявленный тип для этого имени. Имена представляют собой lvalue-выражения, но это не влияет на поведение >decltype. Однако для lvalue-выражений, более сложных, чем имена, >decltype гарантирует, что возвращаемый тип всегда будет lvalue-ссылкой. Иначе говоря, если lvalue-выражение, отличное от имени, имеет тип , то >decltype сообщает об этом типе как об >Т&. Это редко на что-то влияет, поскольку тип большинства lvalue-выражений в обязательном порядке включает квалификатор lvalue-ссылки. Например, функции, возвращающие lvalue, всегда возвращают lvalue-ссылки.

Однако у этого поведения есть следствия, о которых необходимо знать. В коде

>int x = 0;

>x является именем переменной, так что >decltype(x) представляет собой >int. Однако “заворачивание” имени >x в скобки — “>(x)” — дает выражение, более сложное, чем имя. Будучи именем, >x представляет собой lvalue, и С++ также определяет выражение >(x) как lvalue. Следовательно, >decltype((x)) представляет собой >int&. Добавление скобок вокруг имени может изменить тип, возвращаемый для него >decltype!

В C++11 это просто любопытный факт, но в сочетании с поддержкой в С++14 >decltype (auto) это означает, что, казалось бы, тривиальные изменения в способе записи инструкции >return могут повлиять на выводимый тип функции:

>decltype(auto) f1() {

> int x = 0;

> …

> return x;   // decltype(x) представляет собой int,

>}            // так что f1 возвращает int


>decltype(auto) f2() {

> int x = 0;

> …

> return (x); // decltype((x)) представляет собой int&,

>}            // так что f2 возвращает int&

Обратите внимание, что >f2 не только имеет возвращаемый тип, отличный от >f1, но и возвращает ссылку на локальную переменную! Этот код ведет вас к неопределенному поведению, что вряд ли является вашей целью.

Основной урок состоит в том, чтобы при использовании >decltype(auto) уделять деталям самое пристальное внимание. Кажущиеся совершенно незначительными детали в выражении, для которого выводится тип, могут существенно повлиять на тип, возвращаемый >decltype(auto). Чтобы гарантировать, что выводимый тип — именно тот, который вы ожидаете, используйте методы, описанные в разделе 1.4.

В то же время не забывайте и о более широкой перспективе. Конечно, >decltype (как автономный, так и в сочетании с >auto) при выводе типов иногда может привести к сюрпризам, но это не нормальная ситуация. Как правило, >decltype возвращает тот тип, который вы ожидаете. Это особенно верно, когда >decltype применяется к именам, потому что в этом случае >decltype делает именно то, что скрывается в его названии: сообщает объявленный тип (declared type) имени.

Следует запомнить

• >decltype почти всегда дает тип переменной или выражения без каких-либо изменений.

• Для lvalue-выражений типа , отличных от имени, >decltype всегда дает тип >T&.

• C++14 поддерживает конструкцию >decltype(auto), которая, подобно >auto, выводит тип из его инициализатора, но выполняет вывод типа с использованием правил >decltype.

1.4. Как просмотреть выведенные типы

Выбор инструментов для просмотра результатов вывода типа зависит от фазы процесса разработки программного обеспечения, на которой вы хотите получить эту информацию. Мы рассмотрим три возможности: получение информации о выводе типа при редактировании кода, во время компиляции и во время выполнения.