Шаблон псевдонима, частичная специализация и недопустимый тип параметра void

Рассмотрим следующий код:

template<typename F>
struct S;

template<typename Ret, typename... Args>
struct S<Ret(Args...)> { };

template<typename... Args>
using Alias = S<void(Args...)>;

int main() {
    S<void(int)> s;
    Alias<int> alias;
}

Он работает нормально, как и ожидалось, и обе строки с участием S и тот, который включает Alias определить под капотом тот же тип S<void(int)>,

Теперь рассмотрим следующие изменения:

int main() {
    S<void(void)> s;  // this line compiles
    Alias<void> alias;  // this line does not
}

Я ожидал, что он скомпилируется, по причинам, аналогичным приведенным выше.
Само собой разумеется, что он не компилируется из-за строки, включающей Aliasвместо этого я получаю ошибку:

В замене "шаблон с использованием псевдонима = S [с Args = {void}]"

[...]

ошибка: неверный тип параметра 'void'

Вопрос довольно простой: что я здесь пропустил?

1 ответ

Решение

Из [dcl.fct] выделено мое:

Список параметров, состоящий из одного безымянного параметра независимого типа void эквивалентно пустому списку параметров. За исключением этого особого случая, параметр не должен иметь тип cv void,

В этом случае, Args... является зависимым типом пакета, поэтому void там не разрешено Эта идея повторяется в примечании в [temp.deduct]:

[Примечание: удержание типа может завершиться ошибкой по следующим причинам:
- [...]
- Попытка создать тип функции, в которой параметр имеет тип voidили в котором тип возвращаемого значения является типом функции или типом массива.
- [...]
—Конечная записка]

Обратите внимание, что S<void(void)> компилируется с void(void) не зависит и эквивалентно void(), так Ret(Args...) никогда не выводится, чтобы иметь void в списке параметров - выводится с Args... пустой.


По крайней мере, есть простой обходной путь, в котором вы можете просто написать Alias<>,

Другие вопросы по тегам