Ответ 1
Специализации для пользовательских типов, как правило, прекрасны и всегда были. N4606, [namespace.std]/1:
Программа может добавлять специализацию шаблона для любого стандартного шаблона библиотеки в пространство имен
std
только в том случае, если объявление зависит от пользовательского типа, а специализация соответствует требованиям стандартной библиотеки для исходного шаблона и явно не запрещена.
Для tuple_size
требования к исходному шаблону указаны в [tuple.helper]/1:
Все специализации
tuple_size<T>
должны соответствовать требованиямUnaryTypeTrait
сBaseCharacteristic
ofintegral_constant<size_t, N>
для некоторогоN
.
UnaryTypeTrait
, в свою очередь, в [meta.rqmts]/1:
ТребованияВ UnaryTypeTrait описывается свойство типа. Он должен быть шаблоном класса, который принимает один аргумент типа шаблона и, необязательно, дополнительные аргументы, которые помогают определить описываемое свойство. Он должен быть
DefaultConstructible
,CopyConstructible
и публично и однозначно выводиться прямо или косвенно из его BaseCharacteristic, который является специализацией шаблонаintegral_constant
, с аргументами к шаблонуintegral_constant
, определяемым требованиями для описываемое конкретное свойство. Имена членов BaseCharacteristic не должны быть скрыты и должны быть однозначно доступны в UnaryTypeTrait.
tuple_element
указаны в [tuple.helper]/6 и [meta.rqmts]/3, но в интересах краткости я не буду публиковать их здесь. Достаточно сказать, что действительно законно специализироваться...