Короткое замыкание шаблона специализации в std :: disjunction - PullRequest
0 голосов
/ 22 мая 2018

Немного предыстории:

Я работаю над созданием шаблонного класса, который, как часть специализации шаблона, выводит тип для использования для одного из его членов.Этот элемент данных должен поддерживать потоковую передачу по проводам, и я стараюсь сделать систему максимально гибкой и расширяемой (с целью создания новых вариантов типа путем изменения некоторых высокоуровневых элементов специализации).логика без проникновения в суть кода реализации).Некоторые из существующих применений специализируют этот элемент данных как перечисление, и потоковый код поддерживает преобразование этого значения назад и вперед в 32-разрядное целое число для передачи по проводам.

Поскольку перечисление может быть определено (либо неявно, либо явно) для поддержки другого типа - наиболее опасного в случае 64-битного значения - я хотел бы иметь возможность принудительно установить, что если разрешенный тип является перечислением, его базовый тип должен быть32-разрядное целое число (в общем, мне просто нужно убедиться, что это максимум из 32 битов, но я буду беспокоиться об этой сложности, как только будет работать более простой случай).

Моя попытка решения:

Соединяя некоторые инструменты, предоставляемые type_traits , я придумал следующее:

#include <cstdint>
#include <type_traits>

using TestedType = /* Logic for deducing a type */;
constexpr bool nonEnumOrIntBacked =
    !std::is_enum_v<TestedType>
    || std::is_same_v<std::underlying_type_t<TestedType>, std::int32_t>;
static_assert(nonEnumOrIntBacked,
    "TestedType is an enum backed by something other than a 32-bit integer");

Однако,когда я пытался скомпилировать это (используя Visual Studio 2017 в последнем обновлении), меня встретил текст ошибки 'TestedType': only enumeration type is allowed as an argument to compiler intrinsic type trait '__underlying_type'.Видя это, я попробовал альтернативную формулировку, используя std :: disjunction , которая, как я считаю, должна приводить к короткому замыканию при оценке шаблонов, если более раннее условие оценивается как истинное (я пропустил квалификаторы std для этогонемного более читабельно):

disjunction_v<
    negation<is_enum<TestedType>>,
    is_same<underlying_type_t<TestedType>, int32_t>
>;

Я также пытался обернуть оскорбительное использование underlying_type_t внутри enable_if, основанного на типе enum, но также не смог с этим справиться.

Мой вопрос :

Не являются ли булевы операторы вообще (и std::disjunction в частности) оценкой шаблонов при коротком замыкании?На странице cppreference для std :: disjunction говорится следующее (выделено мое):

Разъединение имеет короткое замыкание : если естьаргумент типа шаблона Bi с bool (Bi :: value)! = false, тогда создание экземпляра disjunction :: value не требует создания экземпляра Bj :: value для j> i

Читая это, я ожидал бы, что неправильно сформированная природа underlying_type_t<TestedType> для некоторого не перечислимого типа не будет иметь значения, так как нижестоящие типы не должны рассматриваться, когда что-то вверх по течению было оценено как истинное.1050 * Если мое чтение спецификации неверно по этому вопросу, есть ли другой способ выполнить эту проверку во время компиляции, или мне нужно будет добавить проверку во время выполнения, чтобы применить это?

Ответы [ 3 ]

0 голосов
/ 22 мая 2018

Крис объяснил , почему конструкция потерпела неудачу, и дал решение.Но в духе использования каждой отдельной функции библиотеки, вот как использовать короткое замыкание

template<typename T, typename I>
struct is_underlying
{
    static constexpr auto value =
        std::is_same_v<std::underlying_type_t<T>, I>;
};

using TestedType = int;
constexpr bool nonEnumOrIntBacked =
    std::disjunction_v<std::negation<std::is_enum<TestedType>>, 
        is_underlying<TestedType, std::int32_t>>;

Шаблон должен быть правильно сформированным, но не value.

0 голосов
/ 22 мая 2018

Альтернатива:

template <typename T> struct identity { using type = T; };

bool nonEnumOrIntBacked =
    std::is_same<
         std::conditional_t<
             std::is_enum_v<TestedType>,
             std::underlying_type<TestedType>,
             identity<void>>::type,
         int
    >::value
;

с conditional<cont, type1, type2>::type::type :) для задержки оценки.

0 голосов
/ 22 мая 2018

Аргументы шаблона «оцениваются» так же, как обычные аргументы.Часть, вызывающая проблемы - underyling_type_t, но она присутствует полностью в обеих версиях.Вам нужно отложить эту часть до тех пор, пока вы не узнаете, что TestedType является типом enum.Это довольно просто для constexpr, если ( живой пример ):

template<typename T>
constexpr bool nonEnumOrIntBackedImpl() {
    if constexpr (std::is_enum_v<T>) {
        return std::is_same_v<std::underlying_type_t<T>, std::int32_t>;
    } else {
        return false;
    }
}

template<typename T>
constexpr bool nonEnumOrIntBacked = nonEnumOrIntBackedImpl<T>();

До C ++ 17 одним из распространенных методов является диспетчеризация тегов ( живой пример ):

template<typename T>
constexpr bool nonEnumOrIntBackedImpl(std::true_type) {
    return std::is_same<std::underlying_type_t<T>, std::int32_t>{};
}

template<typename T>
constexpr bool nonEnumOrIntBackedImpl(std::false_type) {
    return false;
}

template<typename T>
constexpr bool nonEnumOrIntBacked = nonEnumOrIntBackedImpl<T>(std::is_enum<T>{});
...