std :: disjunction

时间:2018-05-22 18:28:47

标签: c++ templates visual-c++ c++17 static-assert

一些背景:

我正在努力整理一个模板化的类,作为模板特化的一部分,它推导出一个类型用于其中一个成员。这个数据成员需要支持通过线路流式传输,并且我试图保持系统尽可能灵活和可扩展(目标是可以通过修改专业化的一些高级元素来创建该类型的新变体逻辑没有进入实现代码的内容)。一些现有的用法将此数据成员专门化为枚举,并且流代码支持将此值来回转换为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内,该std::disjunction谓词类型为enum,但也没有成功。

我的问题

一般来说布尔运算符(特别是underlying_type_t<TestedType>)是不是对模板进行短路评估?在the cppreference page for std::disjunction上,它说明了以下内容(强调我的):

  

析取是短路的:如果模板类型参数Bi与bool(Bi :: value)!= false,则实例化disjunction :: value 不需要实例化对于j的Bj ::值> I

读到这一点,我原本预计def login(username, password): try: l.simple_bind_s(username, password) base = "OU=Locations,DC=mydoamin,DC=com" criteria = "(&(objectClass=user)(physicalDeliveryOfficeName=Office)(whenCreated>=20180510000000.0Z))" attributes = ['sAMAccountName', 'whenCreated'] result = l.search_s(base, ldap.SCOPE_SUBTREE, criteria, attributes) r = str(result[0][1]) print(r) except ldap.INVALID_CREDENTIALS: return False return True login(username,password) 对于某些非枚举类型的不良形式是无关紧要的,因为一旦某些上游被评估为真,就不需要考虑下游类型。

如果我在这一点上对规范的阅读不正确,是否有其他方法可以在编译时完成此检查,或者我是否需要添加运行时检查来强制执行此操作?

3 个答案:

答案 0 :(得分:6)

模板参数像常规参数一样急切地“评估”。导致问题的部分是underyling_type_t,但它在两个版本中都是完整的。在知道TestedType是枚举类型之后,您需要延迟该部分。对于constexpr if(block element):

,这是相当简单的
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之前,一种常见的方法是标记调度(live example):

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>{});

答案 1 :(得分:2)

chris explained为什么构造失败并给出了解决方案。但本着开发库的每个功能的精神,这里是如何使用短路

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

答案 2 :(得分:1)

替代方案:

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 :)延迟评估。

相关问题