Как я могу создать руководства по выводу для псевдонимов шаблонов в C++20?
Предположим, у меня есть шаблон класса / структуры вместе с явным руководством по выводам для его конструктора. Пусть у этого класса есть два параметра шаблона, один из которых может быть выведен руководством по дедукции, а другой - нет.
template <int Q, typename T>
struct Foo {
template <typename F>
Foo(F&&) { }
};
template <typename T>
using alias = T;
template <typename T>
struct alias2 { using type = T; };
template <int Q, typename F>
Foo(F&& f) -> Foo<Q, alias<F>>; // deduction guide, but cannot deduce Q yet
template <typename T>
using Bar = Foo<1, T>; // create alias that fixes Q
/* This should generate a deduction guide for Bar<T> by first
"copying" Foo's deduction guide, deducing from Foo<Q, alias<F>>
and Foo<1, T> that Q=1 and T=alias<F>=F, thus generating
<template F>
Bar(F&&) -> Bar<1, F>;
if this was correct syntax. */
int main() {
Bar f{ 5 };
}
Если я сейчас создам псевдоним, который будет явно указывать ранее невыводимый параметр, насколько я понимаю, неявно сгенерированное руководство по выводу этого псевдонима должно иметь возможность полностью выводить оба аргумента шаблона (по правилам стандартного вывода аргументов шаблона), даже если один тип не определен в определяющем шаблоне класса.
Но что я могу сделать в сценарии, когда я не использую alias
, но alias2
, т. е. измените руководство по выводам на
template <int Q, typename F>
Foo(F&& f) -> Foo<Q, typename alias2<F>::type>;
Согласно документации, теперь это представит невыявленный контекст (поскольку параметр шаблона появляется слева от оператора области::
), поэтому вывод аргумента шаблона для T=F
должен выйти из строя (что, по-видимому, так и есть).
Вопрос 1: Если эта теория верна, что я могу с этим поделать? Предположим, я хочу использовать не тривиальный псевдоним идентичности, а более сложное преобразование типа, которое в конечном итоге будет иметь формуtypename transformation<Input>::result
в руководстве по выводам.
Вопрос 2: Даже сейчас моя теория терпит неудачу, когда я полностью удаляю Q, поскольку следующий код будет принят (GCC-10/11):
template <typename T>
struct Foo {
template <typename F>
Foo(F&&) { }
};
template <typename T>
struct alias2 { using type = T; };
template <typename F>
Foo(F&& f) -> Foo<typename alias2<F>::type>;
template <typename T>
using Bar = Foo<T>;
template <typename T>
void some(typename alias2<T>::type) { }
int main() {
Bar f{ 5 };
}
Почему компилятор может вывести T из F, даже если это невыведенный контекст?
1 ответ
Чтобы делать то, что вы хотите, C++ должен иметь возможность инвертировать подпрограмму, завершенную по Тьюрингу.
Полные по Тьюрингу программы не только необратимы, но и невозможно определить, является ли данная полная по Тьюрингу программа обратимой или нет. Вы можете определять подъязыки, где все они обратимы, но этим подъязыкам не хватает полной мощности по Тьюрингу.
Вычитая Bar
аргумент псевдонима:
template <typename T>
using Bar = Foo<1, T>;
требует инвертирования 2-го аргумента шаблона alias<F>
найти F
. когдаalias
- тривиальный псевдоним шаблона, это возможно, разрешено и требуется стандартом C++.
когда alias2
оценивает foo<F>::type
, такая конструкция способна к полному по Тьюрингу вычислению. Вместо того, чтобы заставлять компиляторы пытаться инвертировать такое вычисление, стандарт C++ единообразно говорит "не пытайтесь". Обычно он использует "зависимый тип", чтобы заблокировать такую попытку инверсии.
Во втором случае Bar
это банальный псевдоним Foo
. Foo
есть руководство по вычету. Эта гильдия вычетов говорит, как получить отF
к T
, поэтому компилятору не нужно инвертировать какие-либо потенциально полные по Тьюрингу программы, чтобы определить T
.
В языке C++ есть множество формулировок, позволяющих псевдонимам шаблонов, которые просто переименовывают параметры или тому подобное, действовать так, как если бы они принадлежали к исходному типу. Изначально даже игрушечный псевдоним блокировал кучу такого рода выводов; но это оказалось плохим планом. Поэтому они добавили в стандарт текст, описывающий, какие псевдонимы шаблонов такие "тривиальные", и изменили формулировку правил дедукции, чтобы они считались прозрачными.
Чтобы инвертировать произвольную полную по Тьюрингу программу (фактически, почти любое структурно нетривиальное преобразование типа) во время вывода типа, вы должны явно указать инверсию.
Как только вы это примете, это станет битвой с синтаксисом, а не концептуальной.
Мне неизвестно текущее состояние руководств по выводу пользовательских шаблонов из псевдонимов. Последнее, что я слышал, это не поддерживается, но в последнее время не проверял.