Специализация шаблона с аргументом по умолчанию
У меня есть программа, которая заключается в следующем. Есть базовый шаблон struct X
и частичная специализация с SFINAE.
template <typename T, typename U = void>
struct X{
X() {
std::cout << "in 1" << std::endl;
};
};
template <typename T>
struct X< T, std::enable_if_t<std::is_integral_v<T>> > {
X() {
std::cout << "in 2" << std::endl;
};
};
int main() {
X<int> x;
}
При запуске программы in 2
печатается.
Почему вторая специализация выбрана первой, так как они фактически объявляют
struct X<int, void>
, Что делаетstd::enable_if_t<std::is_integral_v<T>>
более специализированный, чем аргумент типа шаблона по умолчанию, как показано в базовом шаблоне?Почему аргумент типа по умолчанию базового шаблона должен совпадать с типом, определенным частичной специализацией для вызываемой частичной специализации, и
in 2
быть напечатанным. Почему меняется наstd::enable_if_t<std::is_integral_v<T>, bool>
вызвать базовый шаблонin 1
быть названным?
2 ответа
Ответы на ваши вопросы находятся в шаблоне частичного заказа. Это механизм, который компилятор использует для определения того, какой шаблон лучше всего подходит (будь то перегрузка шаблона функции или, в вашем случае, специализация шаблона класса).
Вкратце, ваша реализация шаблона имеет 2 параметра T
а также U
в то время как ваша специализация SFINAE имеет только T
параметр, а второй выводится из T
, Поэтому он более специализирован, чем общий случай, и в конце концов, когда вы ссылаетесь на X<int, void>
, специализация выбрана.
Теперь вопрос 2. Предположим, мы заменим enable_if
параметр с bool
вместо void
, Теперь наша специализация будет X<int, bool>
вместо X<int, void>
поэтому, когда вы ссылаетесь на X<int>
т.е. X<int, void>
больше не соответствует специализации, потому что это 2 разных типа.
1) [...] Что делает std::enable_if_t> более специализированным, чем аргумент типа шаблона по умолчанию, как показано в базовом шаблоне?
Итак, знаете ли вы, что, если два шаблона совпадают, выбирается более специализированный.
Ну... второй более специализированный, потому что если X
соответствует специализации (так что если X
является целочисленным типом), это также соответствует общей версии.
Но существуют пары типов (например: std::string
, void
), которая соответствует универсальной версии и не соответствует специализации.
Таким образом, специализация более специализирована, чем универсальная версия, поэтому предпочтительнее, когда оба шаблона совпадают.
Почему аргумент типа по умолчанию базового шаблона должен совпадать с типом, определенным частичной специализацией для частичной специализации, которая будет вызвана и в 2 должна быть напечатана. Почему изменение на std::enable_if_t, bool> вызывает базовый шаблон в 1 для вызова?
Вы должны понять, как работает прием значения по умолчанию.
У вас есть это общая версия, которая
template <typename T, typename U = void>
struct X;
так что писать X<int> x
пишешь X<int, void> x;
и обязательно соответствует универсальной версии.
Специализация
template <typename T>
struct X< T, std::enable_if_t<std::is_integral_v<T>>>;
Вопрос: X<int, void>
Матчи X< T, std::enable_if_t<std::is_integral_v<T>>>
?
Ответ: да, потому что int
является интегральным, так std::enable_if_t<std::is_integral_v<T>>
замещен void
,
Предположим теперь, что общая специализация становится
template <typename T>
struct X< T, std::enable_if_t<std::is_integral_v<T>, bool>>
У нас есть это X<int> x
опять X<int, void> x
и соответствует снова универсальной версии.
Вопрос: X<int, void>
соответствует также X< T, std::enable_if_t<std::is_integral_v<T>, bool>>
?
Ответ: нет, потому что std::enable_if_t<std::is_integral_v<T>, bool>
становиться bool
а также X<int, void>
не соответствует X<int, bool>
Таким образом, общая версия является единственной, которая соответствует и выбрана.