Может ли std :: tuple_element удваиваться как универсальный шаблонный аргумент? - PullRequest
3 голосов
/ 13 сентября 2010

Этот вопрос заставил меня задуматься. Иногда полезно получить фактический аргумент из специализации шаблона класса, если он не может определить общедоступный 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 Ответ

1 голос
/ 13 сентября 2010

Таким образом, общая специализация не только не должна конфликтовать со специализацией массива, но и не может конфликтовать с какой-либо специализацией, которая именует определенный пользователем тип. То есть, если пользователь объявляет специализацию, существование метода 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 сам по себе не является выводимым контекстом.

Если вычет успешен в течение одного раунда, а не наоборот (... исключая некоторые другие правила, потому что они случаются только для реального упорядочения шаблонов функций), шаблон с правой стороны выше для раунда, который провалил вывод, более специализированные. В противном случае частичные специализации неупорядочены.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...