#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
определяется.
Говорят, что аргументы шаблона
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
.