Допустим, мы хотим, чтобы шаблонная структура part
вызывалась part<U,D>
, где D
имеет тип U
:
template<typename U, U D>
struct part{};
Мы в двух случаях:
U
относится к типу unsigned
, а D%(sizeof(U)*8)==0
U
относится к типу unsigned
и D%(sizeof(U)*8)!=0
Допустимо В случае с guish делаются два члена: value
и divisible
. Базовый случай - условие не выполняется, и для экземпляра struct используются value
и divisible
, оба из которых false
. Если выполнены условия 1), у меня должно быть value=true, divisible= false
, а если выполнены условия 2), у меня должно быть value=true, divisible= true
. Забегая вперед, скажем также, что если выполнены 1) или 2), у меня также есть type
в структуре.
Я настроил вспомогательную структуру choice
для использования в std::enable_if
и выбрал соответствующий случай через фиктивный аргумент шаблона. Код следующий:
// to be used in dummy template arguments
template<size_t D>
struct choice{};
// Base case
template<typename U, U D, typename E1 = choice<0>, typename E2 = choice<0> >
struct part{
static constexpr bool value = false;
static constexpr bool divisible = false;
};
// Specialisation 1 <U, D, choice<1>, choice<0> >
template<typename U, U D>
struct part<U,D,std::enable_if_t<std::is_unsigned<U>::value, choice<1> >, std::enable_if_t<!(D%(sizeof(U)*8)), choice<0> > >{
static constexpr bool value = true;
static constexpr bool divisible = false;
typedef U type;
};
// Specialisation 2 <U, D, choice<1>, choice<1> >
template<typename U, U D>
struct part<U,D,std::enable_if_t<std::is_unsigned<U>::value, choice<1> >, std::enable_if_t<(D%(sizeof(U)*8)), choice<1> > >{
static constexpr bool value = true;
static constexpr bool divisible = true;
typedef U type;
};
Компилируется. Отлично. Давайте посмотрим, что мы сделали с простой печатью:
std::cout << "Should get 0,0 "<< std::endl;
std::cout << part<int,5>::value << std::endl;
std::cout << part<int,5>::divisible << std::endl;
std::cout << "Should get 1,0 "<< std::endl;
std::cout << part<uint16_t,5>::value << std::endl;
std::cout << part<uint16_t,5>::divisible << std::endl;
std::cout << "Should get 1,1 "<< std::endl;
std::cout << part<uint16_t,16>::value << std::endl;
std::cout << part<uint16_t,16>::divisible << std::endl;
std::cout << "Should get 1,0 "<< std::endl;
std::cout << part<uint16_t,30>::value << std::endl;
std::cout << part<uint16_t,30>::divisible << std::endl;
std::cout << "Should get 1,1 "<< std::endl;
std::cout << part<uint16_t,32>::value << std::endl;
std::cout << part<uint16_t,32>::divisible << std::endl;
// RESULTS ON TERMINAL ARE:
//
// Should get 0,0
// 0
// 0
// Should get 1,0
// 0
// 0
// Should get 1,1
// 0
// 0
// Should get 1,0
// 0
// 0
// Should get 1,1
// 0
// 0
Так что совсем не то, что я хочу. Хорошо, давайте попробуем:
std::cout << std::is_same<part<uint16_t,5>::type, uint16_t> << std::endl;
// COMPILATION ERROR:
src/tests/test1.cpp(55): error: class "part<uint16_t={unsigned short}, (uint16_t={unsigned short})5U, choice<0UL>, choice<0UL>>" has no member "type"
std::cout << std::is_same<part<uint16_t,5>::type, uint16_t> << std::endl;
Где я иду не так? Код компилируется, поэтому SFINAE разрешает специализации. Но швы это всегда выбирают базовый вариант. Почему?