Почему требуется `void` при объявлении шаблона класса, который должен быть специализирован, используя` enable_if` - PullRequest
0 голосов
/ 29 марта 2020

Я изучаю метапрограммирование на C ++ и наткнулся на простую проблему, связанную с SFINAE (я так считаю). В частности, я пишу шаблонный класс, который даст нам тип с наибольшим размером. Я называю type правильным типом в зависимости от их размера, сравнивая типы с оператором sizeof. Я выбираю правильную специализацию класса, используя enable_if. Я не понимаю, почему void необходимо указывать в качестве значения по умолчанию для class Enable при объявлении шаблона класса, который должен быть специализирован, с использованием enable_if.

Следующий код прекрасно работает

// test.cpp
#include <type_traits>

// void works just fine but if changed to anything else say int, compilation fails!
template < typename L, typename R, class Enable = void > struct MaxTypeT;

template < typename L, typename R >
struct MaxTypeT<L, R, typename std::enable_if< (sizeof(L) >= sizeof(R)) >::type> {
    using type = L;
};

template < typename L, typename R >
struct MaxTypeT<L, R, typename std::enable_if< (sizeof(L) < sizeof(R)) >::type> {
    using type = R;
};

int main(){
    static_assert(std::is_same< MaxTypeT<int, double>::type, double >::value, "MaxTypeT not working");
    return 0;
}

но когда я заменяю class Enable = void на любой другой тип, скажем class Enable = int, я получаю следующую ошибку. Почему void здесь необходим?

test.cpp: In function ‘int main()’:
test.cpp:17:56: error: incomplete type ‘MaxTypeT<int, double>’ used in nested name specifier
     static_assert(std::is_same< MaxTypeT<int, double>::type, double >::value, "MaxTypeT not working");
                                                        ^~~~
test.cpp:17:56: error: incomplete type ‘MaxTypeT<int, double>’ used in nested name specifier
test.cpp:17:69: error: template argument 1 is invalid
     static_assert(std::is_same< MaxTypeT<int, double>::type, double >::value, "MaxTypeT not working");

Ответы [ 2 ]

2 голосов
/ 29 марта 2020

std::enable_if<>::type по умолчанию void. Таким образом, каждая ваша специализация имеет третий параметр void после замены шаблона.

// primary template
template < typename L, typename R, typename Enable = void > struct MaxTypeT;

// specialization #1
template < typename L, typename R >
struct MaxTypeT<L, R, void> {
    using type = L;
};

// specialization #2
template < typename L, typename R >
struct MaxTypeT<L, R, void> {
    using type = R;
};

Когда вы делаете MaxTypeT<int, double>, он создает тип MaxTypeT<int, double, void>, поскольку для параметра по умолчанию Enable установлено значение void.

Если для Enable установлено значение int, он создает тип MaxTypeT<int, double, int>. Поскольку компилятор не может сопоставить специализацию с этими типами параметров, он идет с основным шаблоном, который был только объявлен и не определен, поэтому возникает ошибка.

Как сказал Тойб Ответ std::enable_if имеет второй параметр шаблона, который указывает, каким будет его член ::type. Если вы установите его на int, то это будет специализация с третьим параметром как int после успешной замены шаблона.

2 голосов
/ 29 марта 2020

То, что void не требуется, просто экономит вам время при наборе текста. Если вы хотите, чтобы int было там, то вам, возможно, следует написать это так:

// test.cpp
#include <type_traits>

// int works just fine now
template < typename L, typename R, typename Enable = int > struct MaxTypeT;


// note additional template argument of enable_if
template < typename L, typename R >
struct MaxTypeT<L, R, typename std::enable_if< (sizeof(L) >= sizeof(R)), int >::type> {
    using type = L;
};

template < typename L, typename R >
struct MaxTypeT<L, R, typename std::enable_if< (sizeof(L) < sizeof(R)), int >::type> {
    using type = R;
};

int main(){
    static_assert(std::is_same< MaxTypeT<int, double>::type, double >::value, "MaxTypeT not working");
    return 0;
}

Второй аргумент enable if по умолчанию void, поэтому для его соответствия вашему шаблону необходимо иметь также void там или для предоставления другого типа enable_if.

...