В языке программирования C++ — это ключевое слово, используемое для запроса типа выражения . Представленный в C++11 , он в основном предназначен для использования в универсальном программировании , где часто сложно или даже невозможно выразить типы, которые зависят от параметров шаблона .decltype
Поскольку в 1990-е годы общие методы программирования становились все более популярными, была признана необходимость в механизме вывода типов. Многие поставщики компиляторов реализовали свои собственные версии оператора, обычно называемые typeof
, а также были разработаны некоторые переносимые реализации с ограниченной функциональностью, основанные на существующих функциях языка. В 2002 году Бьярн Страуструп предложил добавить стандартизированную версию оператора в язык C++ и предложил имя «decltype», чтобы отразить, что оператор будет возвращать «объявленный тип» выражения.
decltype
Семантика была разработана как для разработчиков универсальных библиотек, так и для начинающих программистов. В общем, выведенный тип соответствует типу объекта или функции точно так, как объявлено в исходном коде. Как и оператор sizeof
[1] , decltype
операнд 's не вычисляется.
С появлением шаблонов в языке программирования C++ и появлением общих методов программирования, впервые предложенных Стандартной библиотекой шаблонов , была признана необходимость в механизме получения типа выражения , обычно называемого . typeof
В обобщенном программировании часто бывает сложно или невозможно выразить типы, которые зависят от параметров шаблона, [2] [3] , в частности, типа возвращаемого значения экземпляров шаблона функции. [2]
Многие поставщики предоставляют typeof
оператор как расширение компилятора. [4] Еще в 1997 году, до полной стандартизации C++, Брайан Паркер предложил переносимое решение, основанное на операторе sizeof
. [4] Его работа была расширена Биллом Гиббонсом, который пришел к выводу, что эта техника имеет несколько ограничений и, как правило, менее мощна, чем реальный typeof
механизм. [4] В статье в журнале Dr. Dobb's Journal за октябрь 2000 года Андрей Александреску заметил , что «наличие typeof облегчит написание и понимание кода шаблонов». [5] Он также отметил, что «typeof и sizeof используют один и тот же сервер, потому что sizeof в любом случае должен вычислять тип». [5] Эндрю Кениг и Барбара Э. Му также признали полезность встроенной функции typeof
, с оговоркой, что «их использование часто приводит к тонким ошибкам программирования, и есть некоторые проблемы, которые они не могут решить». [6] Они охарактеризовали использование соглашений о типах, таких как определения типов , предоставляемые стандартной библиотекой шаблонов , как более мощный и общий метод. [6] Однако Стив Дьюхерст утверждал, что такие соглашения «дорогостоящие в разработке и распространении», и что было бы «гораздо проще… просто извлечь тип выражения». [7] В статье о C++0x 2011 года Кениг и Му предсказали, что «decltype будет широко использоваться для облегчения написания повседневных программ». [8]
В 2002 году Бьёрн Страуструп предложил расширить язык C++ механизмами запроса типа выражения и инициализации объектов без указания типа. [2] Страуструп заметил, что семантика удаления ссылок, предлагаемая typeof
оператором, предоставляемым компиляторами GCC и EDG , может быть проблематичной. [2] И наоборот, оператор, возвращающий ссылочный тип на основе lvalue выражения, считался слишком запутанным. Первоначальное предложение комитету по стандартам C++ предусматривало комбинацию двух вариантов; оператор вернет ссылочный тип только в том случае, если объявленный тип выражения включает ссылку. Чтобы подчеркнуть, что выведенный тип будет отражать «объявленный тип» выражения, оператору было предложено дать имя decltype
. [2]
Одним из основных мотивов предложения decltype
была возможность писать идеальные шаблоны функций пересылки . [9] Иногда желательно написать универсальную функцию пересылки, которая возвращает тот же тип, что и обернутая функция, независимо от типа, с которым она создана. Без decltype
, как правило, это невозможно сделать. [9] Пример, в котором также используется тип Trailing-Return : [9]
интервал и foo ( интервал и я ); поплавок foo ( поплавок & f ); шаблон < класс T > auto Transparent_forwarder ( T & t ) − > decltype ( foo ( t )) { return foo ( t ); }
decltype
здесь важен, поскольку он сохраняет информацию о том, возвращает ли обернутая функция ссылочный тип. [10]
Как и в случае с sizeof
оператором, операнд decltype
не оценивается, поэтому выражения типа decltype(i++)
не приводят к увеличению переменной i. [11] Неформально возвращаемый тип decltype(e)
выводится следующим образом: [2]
e
этой переменной или параметра.e
lvalue ,decltype(e)
is T&
, где T
тип e; если e является значением x , результат будет T&&
; в противном случае e является prvalue и результатом будет T
.decltype(auto)
допускается вывод типа, auto
но при этом сохраняется категория значений инициализатора. Точнее, это эквивалентно .decltype(initializer)
Эта семантика была разработана для удовлетворения потребностей разработчиков универсальных библиотек и в то же время была интуитивно понятной для начинающих программистов, поскольку возвращаемый тип decltype
всегда соответствует типу объекта или функции точно так, как объявлено в исходном коде. [2] Более формально, Правило 1 применяется к выражениям идентификаторов без скобок и выражениям доступа к членам класса. [12] [13] Пример: [12]
Примечание относительно добавленных строк для bar(). Ниже типа, выведенного для "bar()", указан простой int, а не const int, поскольку значения prvalue неклассовых типов всегда имеют неквалифицированные типы cv, несмотря на статически объявленный другой тип.
const int && foo (); константный int бар (); интервал я ; структура А { двойной х ; }; const A * a = новый A (); decltype ( foo ())) x1 ; // тип const int&& decltype ( bar ()) x2 ; // тип int decltype ( i ) x3 ; // тип int decltype ( a -> x ) x4 ; // тип — double decltype (( a -> x )) x5 ; // тип — const double&
Причина различия между двумя последними вызовами заключается в decltype
том, что выражение в скобках (a->x)
не является ни выражением идентификатора , ни выражением доступа к члену и, следовательно, не обозначает именованный объект. [14] Поскольку выражение является lvalue, его выведенным типом является «ссылка на тип выражения» или const double&
. [11] Тот факт, что дополнительные круглые скобки вводят к типу ссылочный квалификатор, может стать источником ошибок для программистов, которые не до конца понимают decltype
. [15]
В декабре 2008 года Яакко Ярви выразил комитету обеспокоенность по поводу невозможности использования decltype
для формирования квалифицированного-id , [1] что несовместимо с намерением, которое decltype(e)
следует рассматривать «как если бы это было typedef-name ». [16] Комментируя официальный проект комитета по C++0x , японский член ISO отметил, что «оператор области видимости (::) не может быть применен к decltype, но его следует применять. В этом случае было бы полезно получить тип члена (вложенный тип) из экземпляра следующим образом: [17]
вектор < интервал > v ; decltype ( v ) :: value_type i = 0 ; // int я = 0;
Этот и подобные вопросы, касающиеся формулировки, запрещающей использование decltype
в объявлении производного класса и в вызове деструктора , были рассмотрены Дэвидом Вандевурдом и проголосованы за рабочий документ в марте 2010 года. [18] [19]
decltype
включен в стандарт языка C++, начиная с C++11 . [12] Он предоставляется рядом составителей в качестве расширения. Компиляторы Microsoft Visual C++ 2010 и более поздних версий предоставляют decltype
спецификатор типа, который точно имитирует семантику, описанную в предложении комитета по стандартам. Его можно использовать как с управляемым , так и с собственным кодом. [10] В документации говорится, что это «полезно в первую очередь разработчикам, которые пишут библиотеки шаблонов». [10] decltype
был добавлен в основную ветку компилятора GCC C++ в версии 4.3, [20] выпущен 5 марта 2008 года. [21] decltype
также присутствует в C++ Builder 2009 от Codegear , [22] Intel C++ Compiler , [ 23] и Кланг . [24]