#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()
.