Подтвердить что ты не робот

Шаблон псевдонимов, частичная специализация и недопустимый тип параметра 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, вместо этого я получаю ошибку:

При замене шаблона с использованием Alias ​​= S [с помощью Args = {void}] '

[...]

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

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

4b9b3361

Ответ 1

Из [dcl.fct], мой удар:

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

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

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

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


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