Как написать рекомендации по вычету для псевдонимов шаблонов aggregate?

#c #c 20 #aggregate-initialization #template-aliases #deduction-guide

#c #c 20 #aggregate-инициализация #шаблон-псевдонимы #руководство по вычету

Вопрос:

С C 20 можно создать рекомендации по вычету для шаблона псевдонима (см. Раздел «Вычеты для шаблонов псевдонимов» на https://en.cppreference.com/w/cpp/language/class_template_argument_deduction ). Тем не менее, я не мог заставить их работать с синтаксисом инициализации aggregate. Похоже, что в этом случае рекомендации по вычету для псевдонима не генерируются.

Смотрите этот пример:

 #include <array>

template <size_t N>
using mytype = std::array<int, N>;

// Deduction guideline ???

int main() {
    // mytype error_object = {1, 4, 7}; // ERROR
    mytype<3> object = {1, 4, 7}; // OK, but I have to manually specify the size.
    return object[0];
}
 

Я пытался написать рекомендации по вычету, но каждый раз получаю ошибки компилятора.

 template <typename T, typename ... U>
mytype(T, U...) -> mytype<1 sizeof...(U)>; // Compiler error
 

и любое другое руководство, о котором я мог подумать.

Возможно ли вообще автоматически определить размер псевдонима массива?

Я использую GCC 10.2

Комментарии:

1. Из того, что я прочитал, руководство по вычету для псевдонима использует руководство по вычету из исходного кода с некоторой корректировкой, я не вижу, что написание руководства по вычету для псевдонима разрешено.

Ответ №1:

Возможно ли вообще автоматически определить размер псевдонима массива?

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

Однако GCC реализует набор правил, отличный от того, который указан в стандарте:

 This implementation differs from [the specification] in two significant ways:

1) We include all template parameters of A, not just some.
2) The added constraint is same_type instead of deducible.
 

Разработчик полагал, что «это упрощение должно иметь тот же эффект для реального использования». Но, видимо, это не так: эта реализация не работает в вашем случае и ICEs в некоторых других случаях.


Для справки я постараюсь следовать стандарту и показать, как создается руководство для mytype .

У нас есть это объявление шаблона псевдонима (шаблон псевдонима вызывается A в стандарте):

 template <size_t N>
using mytype = std::array<int, N>;
 

и это руководство по вычету из стандартной библиотеки ([array.cons]):

 template<class T, class... U>
array(T, U...) -> array<T, 1   sizeof...(U)>;
 

Сначала шаблон функции (вызываемый f в стандарте) генерируется из руководства по вычету ([over.match.class.deduct]/1):

 template<class T, class... U>
auto f(T, U...) -> array<T, 1   sizeof...(U)>;
 

Затем за [over.match.class.deduct]/2:

аргументы шаблона возвращаемого типа f выводятся из идентификатора определяющего типа A в соответствии с процессом в [temp.deduct.type], за исключением того, что вывод не завершается ошибкой, если выводятся не все аргументы шаблона.

То есть мы выводим аргументы шаблона array<T, 1 sizeof...(U)> из std::array<int, N> . В этом процессе T выводится как быть int ; U не выводится, поэтому он остается как есть.

Результат вычетов подставляется в шаблон функции, что приводит к:

 template<class T, class... U>
auto g(int, U...) -> array<int, 1   sizeof...(U)>;
 

Затем мы создаем шаблон функции f' . f' имеет тот же возвращаемый тип и типы параметров функции g , что и . (Если f имеет специальные свойства, они наследуются f' .) Но примечательно, что список параметров шаблона f' состоит из ([over.match.class.deduct]/(2.2), курсив мой):

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

Поскольку N он не отображается в вычете, он не включен в список параметров шаблона (в этом GCC отличается от стандарта).

Кроме того, f' имеет ограничение ([over.match.class.deduct]/(2.3)):

это выполняется тогда и только тогда, когда аргументы A выводимы (см. Ниже) из возвращаемого типа.

Поэтому, согласно стандарту, сгенерированный шаблон функции выглядит так:

 template<class... U>
  requires deducible<array<int, 1   sizeof...(U)>>
auto f'(int, U...) -> array<int, 1   sizeof...(U)>;
 

Очевидно, что размер может быть выведен в 1 sizeof...(U) соответствии с этим руководством.

На следующем шаге давайте посмотрим, как deducible определяется.

[over.match.class.deduct]/3:

Говорят, что аргументы шаблона A выводятся из типа T , если задан шаблон класса

 template <typename> class AA;
 

с единственной частичной специализацией, список параметров шаблона которой A соответствует списку параметров шаблона и список аргументов шаблона которого является специализацией A со списком аргументов шаблона A ([temp.dep.type]), AA<T> соответствует частичной специализации.

В нашем случае частичная специализация будет:

  template <size_t N> class AA<mytype<N>> {};
 

So deducible может быть объявлен как:

  template <class T> concept deducible = requires { sizeof(AA<T>); };
 

Поскольку N выводится из 1 sizeof...(U) , array<int, 1 sizeof...(U)> всегда является допустимым совпадением для mytype<N> (a.k.a. std::arrray<int, N> ), и, следовательно, ограничение deducible<array<int, 1 sizeof...(U)>> всегда выполняется.

Поэтому, согласно стандарту, сгенерированное руководство является жизнеспособным и может определять размер.

Для сравнения, GCC генерирует:

 template<class... U, size_t N>
  requires same_type<array<int, 1   sizeof...(U)>, mytype<N>>
auto f_(int, U...) -> array<int, 1   sizeof...(U)>;
 

… который не может вывести N .