TL; DR
Параметры шаблона типа void*
действительны начиная с C ++ 20.Они недействительны до C ++ 20.
C ++ 20
C ++ 20 ослабили ограничения на тип нетипичного параметра шаблона, поэтому давайте сначала исследуем его.
В текущем черновике (по состоянию на UTC 10:00, 6 мая 2019 г.) написано: [temp.param] / 4 :
Нетип шаблон-параметр должен иметь один из следующих (необязательно cv-квалифицированных) типов:
- литеральный тип, имеющий строгое структурное равенство ([класс.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*
имеет строгое структурное равенство .
Следовательно, в текущем черновике C ++ 20, void*
допускается в качестве типа типа параметра шаблона.
C ++ 17
Теперь мы обращаемся к C ++ 17. [temp.param] говорит:
Нетипичный шаблон-параметр должен иметь один из следующих (необязательно квалифицированных cv) типов:
- целочисленный тип или тип перечисления,
- указатель на объект или указатель на функцию,
- lvalue ссылка на объект или lvalue ссылка на функцию,
- указательчлену,
std::nullptr_t
или - тип, который содержит тип заполнителя.
Обратите внимание, что «указатель на объект» неinclude void*
per [basic.compound] / 3 :
[ Примечание: Указатель на void
не имеет указателя на-объект типа, однако, потому что void
не является типом объекта.- конец примечания ]
Ни одна из шести вышеприведенных позиций не включает void*
в качестве возможного типа параметра шаблона.Следовательно, в C ++ 17 параметр шаблона не должен иметь тип void*
.
Формулировка одинакова для C ++ 11 и C ++ 14, за исключением того, что отсутствует маркировка о типах заполнителей.,Как правило, до C ++ 20, параметр шаблона не должен иметь тип void*
.
Но компиляторы это диагностируют?
TC говорит в комментарии , что никто не диагностирует этот IHRC.Давайте проверим, диагностируют ли это компиляторы в режиме C ++ 17 с минимальным примером, показанным ниже:
template <void*>
class C {};
int main()
{
C<nullptr> x;
(void) x;
}
Код компилируется и отлично работает на GCC 9.1.0 , GCC8.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 .
NathanOliver сказал мнев комментарии о том, что кто-то сказал ему, что некоторые компиляторы будут ошибаться, но основные - нет.Поэтому, насколько я могу здесь подтвердить, утверждение ТК верно - никто не ставит диагноз.