Может ли 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 сам по себе не является выводимым контекстом.

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

Другие вопросы по тегам