c++ - std::disjunction 中的短路模板特化

标签 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 里面以枚举类型为前提,但也没有成功。

我的问题:

一般的 bool 运算符(特别是 std::disjunction)不会对模板进行短路评估吗?在 the cppreference page for std::disjunction ,它声明以下内容(强调我的):

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

读到这里,我会预料到 underlying_type_t<TestedType> 的格式错误的性质对于一些非枚举类型是无关紧要的,因为一旦上游的某些东西被评估为真,就不需要考虑下游类型。

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

最佳答案

模板参数就像常规参数一样被急切地“评估”。导致问题的部分是 underyling_type_t,但它在两个版本中都完整存在。您需要延迟该部分,直到您知道 TestedType 是枚举类型。这对于 constexpr if ( live example ) 来说相当简单:

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 之前,一种常见的方法是标记分派(dispatch) ( 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>{});

关于c++ - std::disjunction 中的短路模板特化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50474272/

相关文章:

C++运算符重载解释

c++ - C++类的大小是如何确定的?

c++ - 列出线程 C++

c++ - 64 位构建中 STL/OpenMP 的奇怪并发问题

c++ - 静态链接 MFC 时未修改的 Visual Studio 2012 MFC 模板中的链接错误

c++ - 调用另一个函数重载

c++ - 具有模板模板参数的模板定义,可以专门化为类,例如,std::vector<std::string> 或 std::map<std::tree>

c# - 如果 asp.net 页面中的代码 <% ... %> 在 c# 中获取 RepeaterItem

C++ - 无法推导出模板参数

c++ - GDB:运行没有符号的cpp进程调试