Как написать руководство по дедукции для псевдонимов агрегатных шаблонов?
С помощью C++20 возможно создание руководящих принципов вывода для шаблона псевдонима (См. Раздел "Вывод для шаблонов псевдонимов" на https://en.cppreference.com/w/cpp/language/class_template_argument_deduction). Тем не менее, я не мог заставить их работать с синтаксисом агрегированной инициализации. Похоже, в этом случае руководство по вычету псевдонима не создается.
См. Этот пример:
#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 ответ
Возможно ли автоматическое определение размера псевдонима массива?
Я считаю, что это должно быть возможно при реализации, соответствующей стандарту. Вам не нужно (и нельзя) добавлять дополнительные направляющие.
Однако 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.
Разработчик полагал, что "это упрощение должно иметь такой же эффект для реального использования". Но, очевидно, это не так: эта реализация не работает в вашем случае, а ICE - в некоторых других случаях.
Для справки, я постараюсь следовать стандарту и покажу, как руководство для
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
в стандарте) создается из руководства по вычетам ( http://eel.is/c++draft/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
определено.
http://eel.is/c++draft/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>> {};
Так
deducible
может быть объявлен как:
template <class T> concept deducible = requires { sizeof(AA<T>); };
поскольку
N
выводится из
1 + sizeof...(U)
,
array<int, 1 + sizeof...(U)>
всегда допустимое совпадение для
mytype<N>
(он же
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
.