Шаблон псевдонима, частичная специализация и недопустимый тип параметра 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 [с аргументами = {void}]»

[...]

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

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


person skypjack    schedule 08.03.2016    source источник


Ответы (1)


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

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

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

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

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


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

person Barry    schedule 08.03.2016
comment
Обходной путь очевиден, но разве обе строки не должны компилироваться по одной и той же причине? Также S<void(void)> приводит к выводу, что это попытка (хорошо, успешная) создать тип функции, в котором параметр имеет тип void. Я ошибся? - person skypjack; 09.03.2016
comment
@skypjack Только что нашел раздел, который искал. void должен быть независимым. - person Barry; 09.03.2016
comment
Спасибо за ссылку. В любом случае, разве Args в template<typename Ret, typename... Args> struct S<Ret(Args...)> { }; не является зависимым типом, который должен страдать от той же проблемы? - person skypjack; 09.03.2016
comment
@skypjack Думаю, у вас все в порядке, поскольку вы явно передаете void(void). Не уверен на 100%, в любом случае - person Barry; 09.03.2016
comment
Вы знаете, я создаю новый вопрос для этого, так что начните искать правильную часть ссылки! :-) - person skypjack; 09.03.2016
comment
@skypjack Обновлено, чтобы прояснить, почему void(void) работает. - person Barry; 09.03.2016
comment
Спасибо!! Я также открыл для этого вопрос, если вы хотите принять участие, было бы здорово. :-) - person skypjack; 09.03.2016
comment
К сожалению, я не могу использовать Alias<> в своей реальной проблеме, потому что она сложнее, чем эта, но, возможно, я создаю для нее новый вопрос. - person skypjack; 10.03.2016