Почему `SFINAE` (std :: enable_if) использует литералы bool вместо классов тегов` true_t` / `false_t`? - PullRequest
0 голосов
/ 19 декабря 2018

Я пытаюсь узнать о SFINAE (я следую этому учебнику ), но есть некоторые ... "варианты дизайна", которые я не понимаю, и, как таковые, я нахожу их запутанными.

Давайте предположим, что у меня есть такая ситуация (включенная повторная реализация std::enable_if, просто чтобы продемонстрировать, как я понимаю enable_if)

// A default class (class type) declaration. Nothing unusual.
template <bool, typename T = void>
struct enable_if
{}; 

// A specialisation for <true, T> case. I understand 'why-s' of this. 
// -- 'why-s': if I attempt to access 'enable_if<false, T>::type' (which does not exist) I will get a substitution failure and compiler will just "move-on" trying to match "other cases".
template <typename T>
struct enable_if<true, T> {
    typedef T type;
};

// Here lies my problem:
template <class T,
         typename std::enable_if<std::is_integral<T>::value,T>::type* = nullptr>
void do_stuff(T& t) { /* do stuff */ };


(1) Самая первая вещь, с которой у меня возникает «проблема», это bool литерал (true / false).Я понимаю, что они верны, и шаблоны могут принимать значения констант во время компиляции примитивных типов данных (простые-старые типы данных), но если бы мне было поручено разработать enable_if «механизмы» вместо использования true / falseЯ бы создал теги классов true_t (или True) и false_t (или False) следующим образом:

class true_t {}; // or True

class false_t {}; // or False

template<typename T>
class is_integral // just to have "something" to use with "enable_if"
{
    using result = false_t;
};

template<>
class is_integral<int32_t> // same with all other int types
{
    using result = true_t;
};

template <typename B, typename T = void>
struct enable_if
{}; 

template <typename T>
struct enable_if<true_t, T>
{
    using type = T;
};


(2) Второе, что я считаю излишним, - это необходимость указать typename T параметр шаблона.Разве не было бы проще / лучше просто реализовать enable_if следующим образом:

template <typename B>
struct enable_if
{}; 

template <>
struct enable_if<true_t>
{
    using type = void; // the 'type' exists therefore substitution failure will not occur. 
};


Я хорошо знаю, что все мои предложения крайне уступают существующим в настоящее время решениям , но я не понимаю, почему ... Какую часть функциональности (важной функциональности) текущего SFINAE я сбрил?(Даже не осознавая этого ...)


Я знаю, что на этом сайте я обязан задать один вопрос в формате ... единого типа «вопрос-пост-подобный», ноесли вы сочтете это приемлемым, могу ли я также спросить, чего достигнет этот синтаксис:

std::enable_if</* ... */>::type* = nullptr

?Сейчас это за пределами моего понимания ...

Ответы [ 2 ]

0 голосов
/ 19 декабря 2018

Во-первых, существуют типы тегов для true и false, а именно std::true_type и std::false_type.

Допустим, мы заставили enable_if работать с этим вместо параметра bool,После этого вы больше не сможете делать такие вещи, как std::enable_if<1 == 1>::type, поскольку 1 == 1 оценивается как бул.Так же, как и большинство вещей, которые вы захотите проверить здесь.

С другой стороны, существующие типы тегов могут использоваться в enable_if, поскольку они содержат value и имеют operator(), возвращающие указанное значение.значение.

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

Для пункта 2 это простоудобство, позволяющее указывать, какой тип вы хотите enable_if держать, если это правда.По умолчанию это void, но если вы хотите, вы можете легко получить int, double и т. Д.что иногда может быть полезно.

0 голосов
/ 19 декабря 2018

Самая первая вещь, с которой у меня возникает «проблема», это bool литерал (true / false).Я понимаю, что они верны, и шаблоны могут принимать значения констант во время компиляции примитивных типов данных (простые-старые типы данных), но если бы мне было поручено разработать enable_if "механизмы" вместо использования true / falseЯ хотел бы создать классы тегов true_t (или True) и false_t (или False) следующим образом

Проблема с использованием типа тега вместо просто bool заключается в том, чтоВы должны добавить дополнительную сложность к коду.Если вы хотите проверить условие времени компиляции, например, sizeof, вы не можете просто выполнить sizeof(T) == 8.Вам придется создать абстракцию, которая выполняет проверку и возвращает соответствующий тип тега.

Второе, что я считаю избыточным, - это необходимость указать typename T параметр шаблона.Не было бы проще / лучше просто реализовать enable_if следующим образом

Не совсем.Что если вы хотите использовать SFINAE для типа возвращаемого значения?Тогда у вас будет только функция void, которая неоправданно ограничивает.Вместо этого вы можете использовать то, что позже было добавлено в C ++ 14 и C ++ 17, и создавать псевдонимы.Это делает имена не зависимыми и позволяет отбрасывать typename

template< bool B, class T = void >
using enable_if_t = typename enable_if<B,T>::type;
template< class T >
inline constexpr bool is_integral_v = is_integral<T>::value;

Это позволяет вам переписать

template <class T,
         typename std::enable_if<std::is_integral<T>::value,T>::type* = nullptr>
void do_stuff(T& t) { /* do stuff */ };

в

template <class T,
         std::enable_if_t<std::is_integral_v<T>,T>* = nullptr>
void do_stuff(T& t) { /* do stuff */ };

, хотя я предпочитаюиспользовать bool для типа enable_if_t типа

template <class T,
         std::enable_if_t<std::is_integral_v<T>, bool> = true>
void do_stuff(T& t) { /* do stuff */ };

Я знаю, что на этом сайте я обязан задать один вопрос в пределах ... одного "поста вопроса-подобный »формат, но если вы сочтете его приемлемым, могу ли я также спросить, чего достигнет этот синтаксис:

std::enable_if</* ... */>::type* = nullptr

?

Создает указатель на тип, который std::enable_if "возвращает", и устанавливает его в нулевой указатель.Цель здесь - создать параметр шаблона, который будет существовать, только если условие истинно.Вы можете переписать его в

typename = typename std::enable_if</* ... */>::type

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

...