Может ли std::tuple_element дублироваться как универсальный шаблонный аргумент?
Этот вопрос заставил меня задуматься. Иногда полезно получить фактический аргумент из специализации шаблона класса, если он не может определить публичный 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
?
1 ответ
Таким образом, общая специализация не только не должна конфликтовать со специализацией массива, но и не должна конфликтовать с какой-либо специализацией, которая именует определенный пользователем тип. То есть, если пользователь объявляет специализацию, существование метода get общего аргумента не может повлиять на то, выбран ли он.
Я не понимаю этого. Что вы имеете в виду?
Это нормально для реализации, чтобы выставить общий индексатор шаблона через std::tuple_element?
Это невозможно сделать в общем случае. Представь себе этот
template<int A, char B, long C, class D, int &X, int(*Handler)()>
struct funny_template { };
int x, y();
std::tuple_element<3, funny_template<1, 2, 3, long, x, y> >::type along = 0;
Счастливого макро-метапрограммирования:)
Я не сделал много набегов на правила частичного заказа. Является ли этот анализ правильным?
Частичное упорядочение для двух частичных специализаций
template<class A1, ..., class AN>
class T<Am1, ..., AmM>;
template<class B1, ..., class BL>
class T<Bm1, ..., BmM>;
Работает как преобразование их в шаблоны функций и упорядочение их
template<class A1, ..., class AN>
void f(T1<Am1, ..., AmM>);
template<class B1, ..., class BL>
void f(T2<Bm1, ..., BmM>);
Частичное упорядочение, как показывает ваш анализ, правильно ставит уникальные типы для каждого параметра шаблона, превращая его в аргумент для вывода аргумента, сравнивая его с другим шаблоном
T1A<Uam1, ..., UAmM> -> T2<Bm1, ..., BmM>
T2A<UBm1, ..., UBmM> -> T1<Am1, ..., AmM>
Если есть не выведенный контекст, он не сравнивается как обычно. Как если бы BmM
является typename Foo::X
первый вывод, приведенный выше, рассматривал бы вывод последнего подтипа как успех, потому что не может быть несоответствия для невыраженного контекста. И наоборот, Foo::X
против AmM
может не соответствовать, хотя, если AmM
сам по себе не является выводимым контекстом.
Если вычет успешен в течение одного раунда, а не наоборот (... исключая некоторые другие правила, потому что они происходят только для упорядочения шаблонов реальной функции), шаблон с правой стороны выше для раунда, который не прошел вычет, является более специализированным. В противном случае частичные специализации неупорядочены.