简体   繁体   English

std :: disjunction中的短路模板专业化

[英]Short-circuiting template specialization in std::disjunction

Some Background: 一些背景:

I'm working on putting together a templated class which, as part of template specialization, deduces a type to use for one of its members. 我正在努力整理一个模板化的类,作为模板特化的一部分,它推导出一个类型用于其中一个成员。 This data member needs to support being streamed over the wire, and I'm trying to keep the system as flexible and extensible as possible (with the goal that new variants of the type can be created by modifying some high-level elements of the specialization logic without getting into the guts of the implementation code). 这个数据成员需要支持通过线路流式传输,并且我试图保持系统尽可能灵活和可扩展(目标是可以通过修改专业化的一些高级元素来创建该类型的新变体逻辑没有进入实现代码的内容)。 Some of the existing usages specialize this data member to be an enum, and the streaming code supports converting this value back and forth to a 32-bit integer for transmission over the wire. 一些现有的用法将此数据成员专门化为枚举,并且流代码支持将此值来回转换为32位整数以通过线路传输。

Because an enum could be defined (either implicitly or explicitly) to be backed by a different type -- most dangerous in the case being a 64-bit value -- I'd like to be able enforce that if the resolved type is an enum, its underlying type must be a 32-bit integer (more generally, I just need to enforce that it's a maximum of 32 bits, but I'll be worrying about that complexity once the simpler case is working). 因为可以定义(隐式或显式)枚举以由不同类型支持 - 在这种情况下最危险的是64位值 - 我希望能够强制执行,如果解析的类型是枚举,它的底层类型必须是一个32位整数(更一般地说,我只需要强制它最多 32位,但是一旦更简单的情况工作,我会担心这种复杂性)。

My Attempted Solution: 我尝试的解决方案:

Stitching together some of the tools provided by type_traits , I came up with the following: 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");

However, when I tried to compile this (using Visual Studio 2017 on the latest update), I was met with the error text 'TestedType': only enumeration type is allowed as an argument to compiler intrinsic type trait '__underlying_type' . 但是,当我尝试编译它时(在最新更新时使用Visual Studio 2017),我遇到了错误文本'TestedType': only enumeration type is allowed as an argument to compiler intrinsic type trait '__underlying_type' Seeing this, I tried an alternative formulation using std::disjunction , which I believe should short-circuit evaluation of templates if an earlier condition evaluates to true (I've ommitted the std qualifiers to make this a bit more readable): 看到这个,我尝试了一个使用std :: disjunction的替代公式,我认为如果早期条件的计算结果为true,我应该对模板进行短路评估(我已经省略了std限定符以使其更具可读性):

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

I've also tried wrapping the offending usage of underlying_type_t inside an enable_if predicated on the type being an enum, but didn't have success with that either. 我还尝试将imp_用于enable_if内容包含在以枚举类型为underlying_type_tenable_if ,但是也没有成功。

My Question : 我的问题

Do boolean operators in general (and std::disjunction in particular) not short-circuit evaluation of templates? 一般的布尔运算符(特别是std::disjunction )不是模板的短路评估吗? On the cppreference page for std::disjunction , it states the following (emphasis mine): std :: disjunction的cppreference页面上 ,它声明了以下内容(强调我的):

Disjunction is short-circuiting : if there is a template type argument Bi with bool(Bi::value) != false, then instantiating disjunction::value does not require the instantiation of Bj::value for j > i 断开是短路的 :如果模板类型参数Bi与bool(Bi :: value)!= false,则实例化disjunction :: value 不需要实例化Bj :: value for j> i

Reading this, I would have expected the ill-formed nature of underlying_type_t<TestedType> for some non-enum type to be irrelevant, since downstream types don't need to be considered once something upstream has been evaluated as true. 读到这篇文章,我underlying_type_t<TestedType>预计某些非枚举类型的underlying_type_t<TestedType>underlying_type_t<TestedType>不正确是不相关的,因为一旦上游的某些内容被评估为真,就不需要考虑下游类型。

If my reading of the spec is incorrect on this point, is there another way to accomplish this check at compile-time, or will I need to add a runtime check to enforce this? 如果我在这一点上对规范的读取不正确,是否有其他方法可以在编译时完成此检查,或者我是否需要添加运行时检查来强制执行此操作?

Template arguments are "evaluated" eagerly just like regular arguments. 模板参数像常规参数一样急切地“评估”。 The part causing problems is underyling_type_t , but it's present in full in both versions. 导致问题的部分是underyling_type_t ,但它在两个版本中都是完整的。 You need to delay that part until after you know TestedType is an enum type. 在知道TestedType是枚举类型之后,您需要延迟该部分。 This is fairly straightforward with constexpr if ( live example ): 对于constexpr if( 实例 ),这是相当简单的:

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>();

Prior to C++17, one common method is tag dispatching ( live example ): 在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>{});

chris explained why the construct failed and gave a solution. 克里斯解释了为什么构造失败并给出了解决方案。 But in the spirit of exploiting every single feature of the library, here's how to use the short-circuitry 但本着开发库的每个功能的精神,这里是如何使用短路

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>>;

The template needs to be well-formed, but not its value . 模板需要格式良好,但不是它的value

An alternative: 替代:

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
;

with conditional<cont, type1, type2>::type::type :) to delay evaluation. 使用conditional<cont, type1, type2>::type::type :)来延迟评估。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM