Шаблон псевдонима, частичная специализация и недопустимый тип параметра 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
эквивалентно пустому списку параметров. За исключением этого особого случая, параметр не должен иметь тип cvvoid
,
В этом случае, Args...
является зависимым типом пакета, поэтому void
там не разрешено Эта идея повторяется в примечании в [temp.deduct]:
[Примечание: удержание типа может завершиться ошибкой по следующим причинам:
- [...]
- Попытка создать тип функции, в которой параметр имеет типvoid
или в котором тип возвращаемого значения является типом функции или типом массива.
- [...]
—Конечная записка]
Обратите внимание, что S<void(void)>
компилируется с void(void)
не зависит и эквивалентно void()
, так Ret(Args...)
никогда не выводится, чтобы иметь void
в списке параметров - выводится с Args...
пустой.
По крайней мере, есть простой обходной путь, в котором вы можете просто написать Alias<>
,