Как добавить функцию-член шаблонного класса только для определенных типов, используя type_traits | C

#c #c 17 #sfinae

#c #c 17 #sfinae

Вопрос:

 template <typename T>
class A
{
        template <std::enable_if_t<std::is_signed_v<T>, bool> = true>
        constexpr value_type determinant()
        {
            
        }
}
 

Я хочу, чтобы определяющая функция создавалась только тогда, когда тип T имеет подписанный тип
, поэтому я пробую этот код, но когда тип T беззнаковый, компилятор все равно пытается создать экземпляр определяющей функции.

Я не хочу специализировать шаблонную функцию для каждого подписанного типа T .
Я хочу использовать std::is_signed_v признаки типа для упрощения кодов

Ответ №1:

С помощью SFINAE вы можете включать / отключать метод класса, когда проверяемое условие связано с шаблонным параметром самого метода. Не работает при тестировании параметра шаблона класса.

Вы можете решить проблему, пройдя через параметр шаблона метода, который по умолчанию имеет тип параметра шаблона class / struct.

Например

 #include <iostream>
#include <type_traits>

template <typename T>
struct foo
 {
   template <typename U = T,
             std::enable_if_t<std::is_signed_v<U>, bool> = true>
   constexpr std::size_t bar ()
    { return sizeof(T); }
 };

int main() 
 {
   foo<signed int>   fs;
   foo<unsigned int> fu;

   fs.bar(); // compile
   //fu.bar(); // compilation error
 }
 

Проблема этого решения заключается в том, что вы можете «перехватить» его, эксплицируя параметр шаблона

 fu.bar(); // compilation error
fu.bar<int>(); // compile 
 

Чтобы избежать риска перехвата, вы можете (возможны другие решения, но это мое предпочтение) добавить параметр шаблона, отличающийся от типа, перед U

 // .......VVVVVVVV  add this to avoid the hijacking problem
template <int ...,
          typename U = T,
          std::enable_if_t<std::is_signed_v<U>, bool> = true>
constexpr std::size_t bar ()
 { return sizeof(T); }

// ...

fu.bar(); // compilation error
fu.bar<int>(); // compilation error
fu.bar<1, 2, 3, int>(); // compilation error
 

Комментарии:

1. Ха, эта техника защиты от перехвата — это круто. Будем иметь это в виду. Только сообщение об ошибке немного посредственное 🙂

2. @florestan — Да, это очень круто. К сожалению, я не помню, кто мне это предложил.

3. Меня бы заинтересовали другие методы. Возможно, я мог бы открыть вопрос?

4. @florestan — Есть еще один очевидный метод: добавить std::is_same_v<T, U> условие в тест.

5. @florestan — static_assert() решение хорошее, но не SFINAE. Я имею в виду … с static_assert() вами может возникнуть серьезная ошибка, если вы вызовете метод в неправильном классе. С SFINAE у вас есть мягкая ошибка, поэтому вы можете разработать несколько альтернативных методов: один для подписанных типов, один для неподписанного типа, в данном случае. Вы не можете разработать альтернативные методы, используя static_assert() .