Этот вопрос заставил меня задуматься. Иногда полезно получить фактический аргумент из специализации шаблона класса, если он не может определить общедоступный typedef
аргумента. В C ++ 03 это признак либо плохого дизайна шаблона, либо противоречивого замысла дизайна, и это не особенно распространено. Но вариационные шаблоны делают невозможным охват typedef, поэтому было бы неплохо иметь инструмент для решения проблемы без дополнительной работы.
C ++ 0x решает проблему typedef
для одного конкретного шаблона переменной tuple
.
tuple_element< 2, tuple< char, short, int > >::type my_int; // nth element type
Но tuple_element
не женат на tuple
; он также работает с pair
и array
. В его декларации не упоминается tuple
.
template< size_t index, typename something_like_a_tuple >
struct tuple_element; // general case is left incomplete, unimplemented
tuple
связано с частичной специализацией:
template< size_t index, typename ... tuple_elements >
struct tuple_element< index, tuple< tuple_elements ... > > { // impl. in here
Но так не должно быть. Параметр шаблона шаблона может соответствовать tuple
, как и любой другой шаблон, параметризованный только для типов.
template< size_t index,
template< typename ... > class template_over_types,
typename ... types >
struct tuple_element< index, template_over_types< types ... > > {
Это позволило бы
tuple_element< 1, almost_any_template< char, int > >::type my_int;
tuple_element< 0, pair< int, char > >::type another_int; // same specialization
и пока разрешить дополнительную специализацию для array
template< size_t index, typename element, size_t extent >
struct tuple_element< index, array< element, extent > >
{ typedef element type; }
Конфликт невозможен, поскольку вторым аргументом array
является size_t
, а не тип.
К сожалению, пользователю разрешено специализировать интерфейс tuple_element
для своих собственных типов. Предварительное условие пользователя и его гарантия даны в C ++ 0x §17.6.3.2.1 / 1:
Программа может добавить специализацию шаблона для любого шаблона стандартной библиотеки в пространство имен std, только если объявление зависит от типа, определенного пользователем, и специализация соответствует требованиям стандартной библиотеки для исходного шаблона и явно не запрещена.
Таким образом, общая специализация не только не должна конфликтовать со специализацией array
, она не может конфликтовать со специализацией любой , в которой указан пользовательский тип. То есть, если пользователь объявляет специализацию, существование метода get общего аргумента не может повлиять на то, выбран ли он.
Когда возникает неопределенность в реализации (то есть две частичные специализации соответствуют списку аргументов), альтернативы сравниваются, чтобы определить, какая из них является наиболее специализированной, другими словами, наименее обобщенной. Назовите альтернативы A и B. Это означает, что, если A может выполнять работу B, но B не может выполнять работу A, то B более специализирован. А является универсалом. Б будет выбран. Фактические аргументы, инициирующие создание экземпляров, не рассматриваются, поскольку они, как известно, соответствуют обоим кандидатам.
Поскольку мы хотим, чтобы общий шаблон откладывался на все остальное, мы в хорошей форме.
Общность проверяется путем замены параметров частичной специализации в A уникальными фиктивными типами и проверки, может ли B также реализовать такую специализацию. Повторите эти действия с переставленными ролями, и если будет получен противоположный результат, один кандидат, как известно, будет более специализированным.
Существование пользовательского типа в пользовательской специализации гарантирует его приоритет, поскольку в аргументе-получателе должен быть соответствующий уникальный фиктивный тип, который не будет ему соответствовать.
Например, вот очень общая заявленная пользователем специализация. Он определяет tuple_element
для любого параметризованного шаблона, содержащего данный user_type
.
template< size_t I,
template< typename ... > class any_template,
typename ... T, typename ... U >
struct tuple_element< I, any_template< T ..., ::user_type, U ... > >;
Последовательность ..., user_type, ...
может обрабатываться общим регистром, но регистр пользователя не может обрабатывать последовательность, состоящую полностью из искусственных уникальных типов, потому что она не будет включать user_type
.
Если какая-либо пользовательская специализация является кандидатом, она будет лучшей.
(Стандарт в псевдокоде указывает отдельную частичную специализацию для tuple
, но он может быть опущен в правиле «как если бы». В любом случае, если он будет реализован, он будет охватываться тем же правилом приоритета, что и защита пользователь.)
Я не совершил много попыток в правилах частичного заказа. Является ли этот анализ правильным? Можно ли для реализации предоставить общий индексатор шаблонов через std::tuple_element
?