Ответ 1
TL; DR
Параметры шаблона типа void*
действительны начиная с С++ 20. Они недействительны до С++ 20.
С++ 20
С++ 20 ослабил ограничения на тип нетипичного параметра шаблона, поэтому давайте сначала исследуем его.
В текущем проекте (по состоянию на UTC 10:00, 6 мая 2019 г.) говорится в [temp.param]/4:
Нетипизированный шаблон-параметр должен иметь один из следующих (необязательно квалифицированных cv) типов:
- литеральный тип, имеющий сильное структурное равенство ([class.compare.default]),
- тип ссылки lvalue,
- тип, который содержит тип заполнителя ([dcl.spec.auto]), или
- заполнитель для выведенного типа класса ([dcl.type.class.deduct]).
void*
- это тип указателя. Тип указателя является скалярным типом ([basic.types]/9). Скалярный тип является литеральным типом ([basic.types]/10). Следовательно, void*
является литеральным типом. Первая пуля является актуальной.
Отслеживая далее, [class.compare.default]/3 говорит:
Тип
C
имеет сильное структурное равенство, если при заданном значенииx
типаconst C
либо:
C
- это неклассный тип, аx <=> x
- допустимое выражение типаstd::strong_ordering
илиstd::strong_equality
, или
C
- это тип класса с оператором==
определенным как значение по умолчанию в определенииC
,x == x
правильно сформирован, когда контекстно преобразован вbool
, все подобъекты базового классаC
и нестатические члены-данные имеют сильное структурное равенство, иC
не имеетmutable
илиvolatile
подобъектов.
void*
относится к классу, поэтому первый пункт имеет значение. Теперь вопрос сводится к типу x <=> x
где x
- это glvalue типа void* const
(не const void*
). За [expr.spaceship]/8:
Если тип составного указателя является типом указателя объекта,
p <=> q
имеет типstd::strong_ordering
. Если два операнда-указателяp
иq
сравниваются равными ([expr.eq]),p <=> q
std::strong_ordering::equal
; еслиp
иq
сравниваются неравно,p <=> q
std::strong_ordering::less
еслиq
сравнивает больше, чемp
иstd::strong_ordering::greater
еслиp
сравнивает больше, чемq
([expr.rel]). В противном случае результат не уточняется.
Обратите внимание, что void*
является типом указателя на объект ([basic.compound]/3). Следовательно, x <=> x
имеет тип std::strong_ordering
. Таким образом, тип void*
имеет сильное структурное равенство.
Поэтому в текущем черновике С++ 20 void*
допускается как тип параметра типа шаблона.
С++ 17
Теперь мы обращаемся к С++ 17. [temp.param] говорит:
Нетипизированный шаблон-параметр должен иметь один из следующих (необязательно квалифицированных cv) типов:
- целочисленный или перечислимый тип,
- указатель на объект или указатель на функцию,
- lvalue ссылка на объект или lvalue ссылка на функцию,
- указатель на член,
std::nullptr_t
или- тип, который содержит тип заполнителя.
Обратите внимание, что "указатель на объект" не включает void*
per [basic.compound]/3:
[Примечание: указатель на
void
не имеет типа указатель на объект, потому чтоvoid
не является типом объекта. - конец примечания]
Ни одна из шести вышеприведенных позиций не включает void*
в качестве возможного типа параметра шаблона. Поэтому в С++ 17 параметр шаблона не должен иметь тип void*
.
Формулировка одинакова для С++ 11 и С++ 14, за исключением того, что в ней нет описания типов заполнителей. В общем, до С++ 20 параметр шаблона не должен иметь тип void*
.
Но компиляторы диагностируют это?
В комментарии TC говорится, что никто не ставит диагноз этому IHRC. Давайте проверим, диагностируют ли это компиляторы в режиме С++ 17 с минимальным примером, показанным ниже:
template <void*>
class C {};
int main()
{
C<nullptr> x;
(void) x;
}
Код компилируется и отлично работает на GCC 9.1.0, GCC 8.3.0, GCC 7.3.0, GCC 6.3.0, GCC 5.5.0, Clang 8.0.0, Clang 7.0.0, Clang 6.0.1 и Clang 5.0.0.
Натан Оливер сказал мне в комментарии, что кто-то сказал ему, что некоторые компиляторы будут ошибаться, но основные - нет. Поэтому, насколько я могу подтвердить здесь, утверждение ТС верно - никто не диагностирует это.