Подтвердить что ты не робот

Почему специализация шаблона не выбрана?

Я написал следующий код:

#include <iostream>
#include <string>
#include <type_traits>

template<typename, typename = void>
struct is_incrementable : std::false_type {};

template<typename T>
struct is_incrementable<T, decltype( ++std::declval<T&>() )> : std::true_type {};

int main()
{
    std::cout << is_incrementable<std::string>::value << std::endl;
    std::cout << is_incrementable<int>::value << std::endl;
}

Когда я запустил его, я получаю 0 0. Но я ожидал 0 1.

Любые идеи?

4b9b3361

Ответ 1

Для std::string выбирается первичный шаблон и рассматривается специализация. Но decltype(++std::declval<T&>()) плохо сформирован, поэтому он не рассматривается и используется первичный шаблон (неспециализированный шаблон), что приводит к 0.

Если вы используете int, это становится немного сложнее. Основной шаблон выбирается компилятором, как всегда, и затем рассматривается специализация (потому что специализация всегда считается лучшим совпадением). Специализация <int, int&> для int, но она не соответствует неспециализированному шаблону <int, void> (void является аргументом шаблона по умолчанию), поэтому специализация игнорируется, потому что она не соответствует.

Таким образом, типы параметров шаблона по умолчанию должны совпадать, или специализация не учитывается, так как специализация выполняется только тогда, когда каждый аргумент шаблона соответствует специализации.

Просто добавьте void() в конец, чтобы сделать совпадение специализации для второго параметра шаблона, поскольку левое выражение отбрасывается, а тип void() - void, который соответствует параметру второго шаблона основного шаблона.

template<typename T>
struct is_incrementable<T, decltype( ++std::declval<T&>(), void() )> : std::true_type {};

В С++ 17 вы используете std::void_t для этого.