decltype c что это

Секреты auto и decltype

1. Какой тип будет у переменных ri1..riN после выполнения следующего кода?

Скомпилируются ли следующие фрагменты?

Теория

К механизму вывода типов, используемому в шаблонах в С++11 добавилось два новых механизма: auto и decltype. И чтобы жизнь программистам не казалась медом, все эти 3 механизма выводят типы по-своему. Механизм, используемый auto, в точности копирует механизм шаблонов, за исключением типа std::initializer_list.

Объяснений такому поведению немного и все они не отличаются внятностью. Скотт Мейерс, например, по этому поводу пишет так: “I have no idea why type deduction for auto and for templates is not identical. If you know, please tell me!”. В С++14 этот механизм менять не собираются. За объяснение можно попровать принять тот факт, что работают, например, такие удивительные вещи:

Итак, как же `auto` выводит тип? К сожалению, здесь нет простого правила на все случаи жизни, кроме, пожалуй, того, что `auto` при выводе типа в общем случае отбрасывает cv квалификаторы и ссылки. Ниже я перечислю самые важные моменты.

1.
Если тип some_expression T* или const T*, то тип var также будет T* или const T* соответственно. Пока без сюрпизов. Дальше — интереснее. Пожалуй самое важное с практической точки зрения правило заключается в том, что если тип some_expressionT, const T, T& или const T&, то типом var будет T. Это, впрочем, если задуматься, вполне логично, ведь в этом случае значение, возвращаемое some_expression копируется в var и можно смело писать вот так:

2.
В этом случае, ожидаемо, если тип some_expressionT или const T, компилироваться это не будет, так как lvalue ссылку нельзя инициализировать rvalue. Если тип some_expressionT&, то и var будет иметь тип T&. Здесь важным моментом является то, что если тип some_expressionconst T&, то и тип var будет const T&.

3.
Здесь действует придуманное (или по крайней мере озвученное) Скоттом Мейерсом правило “универсальных ссылок”. Оно заключается в том, что тип var будет зависеть от того какая value category у some_expression. Если rvalue, то тип var будет T&&, если же lvalue, то T&. Cv квалификаторы при этом сохраняются.

Auto как параметр функции

auto нельзя использовать в качестве параметра функции и изменений в этом поведении не предвидется. Очевидно, тут дело в том, что если бы такое было разрешено, то, получается, любую обычную функцию можно было бы объявить по сути неявно шаблонной. И становится непонятно как разрешать перегрузку. Представьте себу такую ситуацию:

Однако в с++14 можно будет использовать auto параметры в лямбдах.

decltype

С decltype ситуация с одной стороны сложнее (если посмотреть формальные правила), с другой стороны проще (если выделить основные моменты). Я сформулирую эти правила так, как я их понял.
Итак, следует различать два основных случая применения decltype.
1. decltype(var), когда var — это объявленная переменная (например в функции или как член класса). В этом случае decltype(var) будет иметь в точности тот тип, с которым объявлена переменная.
2. decltype(expr), expr — выражение. В этом случае типом decltype(expr) будет тип, которое могло бы вернуть это выражение, с той оговоркой, что decltype(expr) будет иметь тип T& (const T&), если expr возвращает lvalue, T, если expr возвращает rvalue типа Т (const T) и T&& (const T&&), если expr возвращает xvalue (rvalue reference).

Что значит “могло бы вернуть”? Это значит то, что decltype не вычисляет переданное ему в качестве аргумента выражение.
Несколько поясняющих примеров:

В том случае, если мы не знаем lvalue нам вернет выражение, rvalue или xvalue, а тип использовать хочется, можно воспользоваться стандартным шаблоном std::remove_reference, чтобы “очистить” тип от ссылок.

Decltype(auto)

Это новая “фишка” языка, которая войдет в С++14. Она нужна для сохранения семантики decltype при объявлении auto переменных и будет использоваться в тех случаях, когда нас не будет устраивать то, что auto отбрасывает ссылки и cv квалификаторы и, возможно, в связке с новой возможностью С++14 — выводом типа возвращаемого функцией значения.

В последнем случае мы могли бы написать decltype(foo()), но представьте, если бы вместо foo() было выражение на 2 строчки, а такие в С++ не редкость.

Ответы

Ну и сейчас, загрузив теорию в кэш, можно попытаться ответить на вопросы теста.

Источник

Статический анализ Си++ кода и новый стандарт языка C++0x

Аннотация
Введение
1. auto
2. decltype
3. Ссылка на временный объект (R-value reference)
4. Правые угловые скобки
5. Лямбда-функции (Lambdas)
6. Suffix return type syntax
7. static_assert
8. nullptr
9. Новые стандартные классы
10. Новые направления в развитии статических анализаторов кода
Заключение
Библиографический список

Аннотация

Введение

Новый стандарт языка Си++ вот-вот придет в нашу жизнь. Пока его продолжают именовать C++0x, хотя, по всей видимости, его окончательное название — C++11. Новый стандарт уже частично поддерживается современными Си++ компиляторами, например Intel C++ и Visual C++. Поддержка далеко не полна, что вполне естественно. Во-первых стандарт еще не принят, а во-вторых даже когда он будет принят, потребуется время на проработку в компиляторах его особенностей.

Читайте также:  congress gov что за сайт

Разработчики компиляторов не единственные, для кого важна поддержка нового стандарта. Нововведения языка оперативно должны быть поддержаны в инструментах статического анализа исходного кода. Новый стандарт обещает обратную совместимость. Почти гарантировано старый Си++ код будет корректно скомпилирован новым компилятором без необходимости каких-либо правок. Однако это не означает, что программа, не содержащая новые конструкции языка, сможет быть по-прежнему обработана статическим анализатором, не поддерживающим новый стандарт C++0x. Мы убедились в этом на практике, попытавшись проверить с помощью PVS-Studio проект, созданный еще в бета-версии Visual Studio 2010. Все дело в заголовочных файлах, в которых уже используются новые конструкции языка. Например, в заголовочном файле «stddef.h» можно увидеть использование нового оператора decltype:
Естественно, что такие конструкции являются синтаксически неверными для анализатора, не поддерживающего C++0x, и приводят, либо к остановке его работы или неверным результатом. Стала очевидной необходимость поддержать C++0x в PVS-Studio к моменту выхода Visual Studio 2010, по крайней мере в том объеме, в котором новый стандарт поддерживается этим компилятором.

Можно заявить, что данная задача нами была успешно решена и на момент написания статьи, на сайте доступна версия PVS-Studio 3.50, интегрирующаяся как в Visual Studio 2005/2008, так и в Visual Studio 2010. Начиная с версии PVS-Studio 3.50 в инструменте реализована поддержка той части С++0x, которая реализована в Visual Studio 2010. Поддержка не идеальна, как например, при работе с «right-angle brackets», но мы продолжим работу по поддержке стандарта C++0x в следующих версиях.

В этой статье мы рассмотрим новые возможности языка, поддержка которых реализована в первой редакции Visual Studio 2010. При этом взглянем на эти возможности с различных позиций: что представляет из себя новая возможность, имеется ли связь с 64-битными ошибками, как новая конструкция языка была поддержана в PVS-Studio и как ее появление отразилось на библиотеке VivaCore.

Примечание. VivaCore — библиотека разбора, анализа и трансформации кода. VivaCore является открытой библиотекой и поддерживает языки Си и Си++. На основе VivaCore построен продукт PVS-Studio и на ее же основе могут быть созданы другие программные проекты.

Предлагаемую вашему вниманию статью можно назвать отчетом по исследованию и поддержке нового стандарта в PVS-Studio. Инструмент PVS-Studio диагностирует 64-битные и параллельные OpenMP ошибки. Но поскольку в данный момент более актуальной темой является переход на 64-битные системы, предпочтение будет отдано примерам, демонстрирующих обнаружение с помощью PVS-Studio 64-битных ошибок.

1. auto

В Си++, как и в Си, тип переменной должен быть указан явно. Однако, с появлением в языке Си++ шаблонных типов и техник шаблонного метапрограммирования, частой стала ситуация, когда тип объекта записать не так просто. Даже в достаточно простом случае, при переборе элементов массива, нам понадобится объявление типа итератора вида:
Подобные конструкции весьма длинны и неудобны. Для сокращения записи можно использовать typedef, но это порождает новые сущности и мало добавляет с точки зрения удобства.

C++0x предлагает способ для смягчения этой проблемы. В новом стандарте значение ключевого слова auto будет заменено. Если раньше auto означало, что переменная создается в стеке, и подразумевалось неявно в случае, если вы не указали что-либо другое (register, к примеру), то теперь это аналог var в C# 3.0. Тип переменной, объявленной как auto, определяется компилятором самостоятельно на основе того, чем эта переменная инициализируется.

Следует заметить, что auto-переменная не сможет хранить значения разных типов в течение одного запуска программы. Си++ по прежнему остается статически типизированным языком, и указание auto лишь говорит компилятору самостоятельно позаботиться об определении типа: после инициализации сменить тип переменной будет уже нельзя.

Теперь итератор может быть объявлен следующим образом:
Помимо удобства в написании кода и его упрощения, ключевое слово auto поможет сделать код более безопасным. Рассмотрим пример, где auto сделает код безопасным с точки зрения создания 64-битных приложений:
Данный код содержит 64-битную ошибку. Функция корректно ведет себя при компиляции Win32 версии и дает сбой при сборке в режиме Win64. Ошибка заключается в использовании типа unsigned для переменной «n», хотя должен использоваться тип string::size_type, который возвращает функция find(). В 32-битной программе тип string::size_type и unsigned совпадают, и мы получаем корректные результаты. В 64-битной программе string::size_type и unsigned перестают совпадать. Когда подстрока не находится, функция find() возвращает значение string::npos, равное 0xFFFFFFFFFFFFFFFFui64. Это значение урезается до величины 0xFFFFFFFFu и помещается в 32-битную переменную. В результате условие 0xFFFFFFFFu == 0xFFFFFFFFFFFFFFFFui64 ложно и получается, что функция Find_Incorrect всегда возвращает true.

В данном примере ошибка не так страшна, так обнаруживается даже компилятором и тем более специализированным анализатором Viva64 (входящим в состав PVS-Studio).

Компилятор:
Viva64:
Важнее то, что данная ошибка возможна и часто встречается в коде из-за неаккуратности при выборе типа для хранения возвращаемого значения. Возможно даже, что ошибка возникла из-за нежелания использовать громоздкую конструкцию вида string::size_type.

Читайте также:  kunpeng антивирусный движок что это

Теперь подобных ошибок легко избежать, при этом не загромождая код. Используя тип «auto» мы можем написать следующий простой и надежный код:
Ошибка исчезла сама собой. Код не стал сложнее или менее эффективным. Вывод — использование «auto» рационально во многих случаях.

Тип переменной «size» автоматически станет «size_t». Но переполнение возникает при вычислении выражения «x * y * z». Это подвыражение имеет тип «int» и только затем тип будет расширен до «size_t» при умножении на переменную «objectSize».

Теперь эту спрятавшуюся ошибку можно будет обнаружить, только используя анализатор Viva64:
Вывод — используя «auto», все-равно следует быть внимательным.

Теперь кратко рассмотрим, как новое ключевое слово было поддержано в библиотеке VivaCore, на которой и построен статический анализатор Viva64. Итак, анализатор должен уметь понять, что переменная AA имеет тип «int», чтобы, предупредить (см. V101) о расширении переменной АА до типа «size_t»:
Прежде всего, была составлена новая таблица лексем, которая включила новые ключевые слова C++0x. Эта таблица находится в файле Lex.cc и имеет имя tableC0xx. Для того чтобы не модифицировать старый код по обработке лексемы «auto» (tkAUTO), лексема «auto» в этой таблице имеет имя tkAUTOcpp0x.

В связи с появлением новой лексемы модификации подверглись следующие функции: isTypeToken, optIntegralTypeOrClassSpec. Появился новый класс LeafAUTOc0xx. В TypeInfoId появился новый класс объектов — AutoDecltypeType.

Для кодирования типа «auto» выбрана литера ‘x’, что нашло отражение в функциях классов TypeInfo и Encoding. Это, например, такие функции как IsAutoCpp0x, MakePtree.

Эти исправления позволяют разбирать код с ключевым «auto», имеющим новый смысл и сохранять тип объектов в закодированном виде (литера ‘x’). Однако это не позволяет узнать, какой тип в действительности представляет переменная. То есть в VivaCore отсутствует функциональность, позволяющая узнать, что в выражении «auto AA = X * Y» переменная AA будет иметь тип «int».

Данная функциональность содержится в исходном коде Viva64 и не включается в состав кода библиотеки VivaCore. Принцип заключается в дополнительной работе по вычислению типа в методе TranslateAssignInitializer. После того, как вычислена правая часть выражения происходит подмена связи (Bind) имени переменной с типом.

2. decltype

В ряде случаев полезно «скопировать» тип некоторого объекта. Ключевое слово «auto» выводит тип, основываясь на выражении, используемом для инициализации переменной. Если инициализация отсутствует, то для определения типа выражения во время компиляции может быть использовано ключевое слово «decltype». Пример кода, где переменная «value» будет иметь тип, возвращаемый функцией «Calc()»:
Можно использовать «decltype» для объявления типа:
Учтите, что тип, взятый с использованием decltype, может отличаться от типа, выведенного с помощью auto.
Перейдем к примеру, где «decltype» может быть полезен с точки зрения 64-битности. Функция IsPresent ищет элемент в последовательности и возвращает «true» если он найден:
Данная функция неспособна работать в 64-битной системе с большими массивами. Если переменная arraySize будет иметь значение больше UINT_MAX, то условие «i >» пока реализован в VivaCore не лучшим образом. В ряде случаев анализатор ошибается и видимо со временем части анализатора, связанные с разбором шаблонов будут нами существенно переработаны. Пока в коде можно увидеть следующие некрасивые функции, которые эвристическими методами пытаются определить, имеем мы дело с оператором сдвига «>>» или с частью объявления шаблонного типа «A > D»: IsTemplateAngleBrackets, isTemplateArgs. Тем, кому интересно, как корректно подойти к решению данной задачи, будет полезен следующий документ: «Right Angle Brackets (N1757)». Со временем мы улучшим обработку правых угловых скобок в VivaCore.

Источник

decltype (C++)

decltype Описатель типа возвращает тип указанного выражения. decltype Спецификатор типа (вместе с decltype ) полезен в основном для разработчиков, создающих библиотеки шаблонов. Используйте auto и decltype для объявления функции шаблона, тип возвращаемого значения которого зависит от типов своих аргументов шаблона. Или используйте auto и decltype для объявления функции шаблона, которая заключает в оболочку вызов другой функции, а затем возвращает тип возвращаемого значения функции с оболочкой.

Синтаксис

decltype( decltype( )

Параметры

expression
Выражение. Дополнительные сведения см. в разделе выражения.

Возвращаемое значение

Комментарии

decltype спецификатор типа поддерживается в Visual Studio 2010 или более поздних версиях и может использоваться с машинным или управляемым кодом. decltype(auto) (C++ 14) поддерживается в Visual Studio 2015 и более поздних версиях.

Для определения типа параметра выражения компилятор использует следующие правила.

Если параметр выражения является идентификатором или доступом к члену класса, то является типом сущности с именем Expression. Если такая сущность или параметр выражения не называет набор перегруженных функций, компилятор выдает сообщение об ошибке.

Если параметр выражения является вызовом функции или перегруженной функции оператора, то аргумент является типом возвращаемого значения функции. Скобки вокруг перегруженного оператора игнорируются.

Если параметр Expression является rvalue, то является типом выражения. Если параметр Expression является левостороннимзначением, то является ссылкой lvalue на тип выражения.

В следующем примере кода демонстрируется использование decltype спецификатора типа. Допустим, во-первых, что были закодированы следующие операторы.

Читайте также:  причиной компрессии какого сегмента позвоночной артерии является добавочное шейное ребро

Затем проверьте типы, возвращаемые четырьмя decltype инструкциями, приведенными в следующей таблице.

Decltype и Auto

В C++ 14 можно использовать decltype(auto) без завершающего возвращаемого типа для объявления функции-шаблона, тип возвращаемого значения которого зависит от типов своих аргументов шаблона.

В C++ 11 можно использовать decltype спецификатор типа для завершающего возвращаемого типа вместе с auto ключевым словом, чтобы объявить функцию шаблона, тип возвращаемого значения которого зависит от типов своих аргументов шаблона. Например, рассмотрим следующий пример кода, в котором тип возвращаемого значения функции шаблона зависит от типов аргументов шаблона. В примере кода неизвестный заполнитель указывает, что тип возвращаемого значения не может быть указан.

auto auto ( ( не следует заменять const const volatile volatile -> decltype( -> opt ) noexcept decltype( < ) >;

Decltype и функции пересылки (C++ 11)

Функции пересылки создают программы-оболочек для вызовов других функций. Рассмотрим шаблон функции, который пересылает свои аргументы (или результаты выражения с этими аргументами) другой функции. Кроме того, функция пересылки возвращает результат вызова другой функции. В этом сценарии тип возвращаемого значения функции пересылки должен совпадать с типом возвращаемого значения функции в программе-оболочке.

Примеры

В следующем примере показана такая ошибка компилятора, возникающая во время объявления.

Требования

Visual Studio 2010 или более поздних версий.

decltype(auto) требуется Visual Studio 2015 или более поздней версии.

Источник

decltype specifier

Compiler support
Freestanding and hosted
Language
Standard library headers
Named requirements
Feature test macros (C++20)
Language support library
Concepts library (C++20)
Diagnostics library
General utilities library
Strings library
Containers library
Iterators library
Ranges library (C++20)
Algorithms library
Numerics library
Localizations library
Input/output library
Filesystem library (C++17)
Regular expressions library (C++11)
Atomic operations library (C++11)
Thread support library (C++11)
Technical specifications
Symbols index
External libraries
Declarators
reference
pointer
array
Block declarations
simple-declaration
→ structured binding declaration (C++17)
alias declaration (C++11)
namespace alias definition
using-declaration
using-directive
static_assert declaration (C++11)
asm-declaration
opaque enum declaration (C++11)
Other declarations
namespace definition
function declaration
class template declaration
function template declaration
explicit template instantiation (C++11)
explicit template specialization
linkage specification
attribute declaration (C++11)
empty declaration

Inspects the declared type of an entity or the type and value category of an expression.

Contents

[edit] Syntax

[edit] Explanation

1) If the argument is an unparenthesized id-expression or an unparenthesized class member access expression, then decltype yields the type of the entity named by this expression. If there is no such entity, or if the argument names a set of overloaded functions, the program is ill-formed.

If the argument is an unparenthesized id-expression naming a structured binding, then decltype yields the referenced type (described in the specification of the structured binding declaration).

If the argument is an unparenthesized id-expression naming a non-type template parameter, then decltype yields the type of the template parameter (after performing any necessary type deduction if the template parameter is declared with a placeholder type). The type is non-const even if the entity is a template parameter object (which is a const object).

If expression is a function call which returns a prvalue of class type or is a comma expression whose right operand is such a function call, a temporary object is not introduced for that prvalue.

Note that if the name of an object is parenthesized, it is treated as an ordinary lvalue expression, thus decltype ( x ) and decltype ( ( x ) ) are often different types.

decltype is useful when declaring types that are difficult or impossible to declare using standard notation, like lambda-related types or types that depend on template parameters.

Источник

Конструкция decltype(auto)

2 ответа 2

auto это «усохший» (decayed) тип выражения, т.е. теряются ссылки и происходит копирование:

auto

В С++11 ключевое слово auto лишено своего первоначального смысла в качестве спецификатора класса хранения и теперь применяется для реализации автоматического выведения типа при условии, что задан явный инициализатор. Компилятор устанавливает тип переменной в тип инициализирующего значения:

можно заменить таким:

decltype

Ключевое слово decltype создает переменную типа, который указан выражением. Приведенный ниже оператор означает «назначить у тот же самый тип, что х», где х представляет собой выражение:

Вот еще пара примеров:

Это особенно полезно в определениях шаблонов, когда тип может быть не определен вплоть до создания специфического экземпляра:

Хвостовой возвращаемый тип

В С++11 появился новый синтаксис для объявления функций, при котором возвращаемый тип указывается после имени функции и списка параметров, а не перед ними:

Новый синтаксис может выглядеть менее читабельным, чем традиционные объявления функций, однако он делает возможным использование decltype для указания возвращаемых типов шаблонных функций:

Иллюстрируемая здесь проблема состоит в том, что когда компилятор читает список параметров ef f, Т и U не находятся в области видимости, поэтому любое использование decltype должно находиться после этого списка параметров. Новый синтаксис делает это возможным.

Источник

Сказочный портал