Как я могу создать руководства по выводу для псевдонимов шаблонов в C ++ 20? - PullRequest
1 голос
/ 14 июля 2020

Предположим, у меня есть шаблон класса / структуры вместе с явным руководством по выводам для его конструктора. Пусть у этого класса есть два параметра шаблона, один из которых может быть выведен с помощью руководства по дедукции, а другой - нет.

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>;

Согласно документации , теперь это будет вводить невыявленный контекст (поскольку параметр шаблона появляется left для оператора области ::), поэтому вывод аргумента шаблона для T=F должен выйти из строя (что очевидно делает ).

Вопрос 1: Если эта теория верна, что я могу с этим поделать? Предположим, я хочу использовать не тривиальный псевдоним идентичности, а более сложное преобразование типа, которое в конечном итоге будет иметь форму typename transformation<Input>::result в руководстве по выводам.

Вопрос 2: Даже теперь моя теория терпит неудачу, когда я полностью удаляю Q, поскольку следующий код будет принят (G CC -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 Ответ

2 голосов
/ 15 июля 2020

Чтобы делать то, что вы хотите, C ++ должен иметь возможность инвертировать подпрограмму, завершенную по Тьюрингу.

Программы, завершенные по Тьюрингу, не только необратимы, но и невозможно определить, полная программа Тьюринга обратима или нет. Вы можете определить подъязыки, где все они обратимы, но этим подъязыкам не хватает полной мощности по Тьюрингу. * найти F. Когда alias является тривиальным псевдонимом шаблона, это возможно, разрешено и требуется стандартом C ++.

Когда alias2 оценивается как foo<F>::type, такая конструкция может быть завершена по Тьюрингу. вычисление. Вместо того, чтобы иметь компиляторы пытаться инвертировать такое вычисление, стандарт C ++ единообразно говорит «не пытайтесь». Обычно он использует «зависимый тип», чтобы заблокировать такую ​​попытку инверсии.

Во втором случае Bar - это тривиальный псевдоним Foo. Foo имеет руководство по вычету. Эта гильдия дедукции сообщает, как получить от F до T, поэтому компилятору не нужно инвертировать какие-либо потенциально полные по Тьюрингу программы, чтобы определить T.

В языке C ++ есть куча формулировок, позволяющих псевдонимам шаблонов, которые просто переименовывают параметры и т.п., действовать так, как если бы они были исходным типом. Первоначально даже игрушечный псевдоним блокировал бы кучу подобных выводов; но это оказалось плохим планом. Поэтому они добавили в стандарт текст, описывающий, какие псевдонимы шаблонов кажутся «тривиальными», и изменили формулировку правил дедукции, чтобы они считались прозрачными.

Чтобы инвертировать произвольную полную по Тьюрингу программу (фактически, почти любое структурно нетривиальное преобразование типа) во время вывода типа, вы должны явно указать инверсию.

Как только вы это приняли, она превращается в битву с синтаксисом, а не с концептуальной.

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

...