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

С помощью 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


person Arjonais    schedule 21.11.2020    source источник
comment
Из того, что я прочитал, руководство по дедукции для псевдонима использует руководство по дедукции источника с некоторой корректировкой, я не вижу, что написание руководства по дедукции для псевдонима разрешено.   -  person Jarod42    schedule 21.11.2020


Ответы (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) создается из руководства по дедукции ([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 выводятся из defining-type-id 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>> {};

Итак, 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.

person cpplearner    schedule 21.11.2020