bio*_*ion 4 c++ templates static-assert visual-c++ c++17
一些背景:
我正在努力整理一个模板化的类,作为模板特化的一部分,它推导出一个类型用于其中一个成员.这个数据成员需要支持通过线路流式传输,并且我试图保持系统尽可能灵活和可扩展(目标是可以通过修改专业化的一些高级元素来创建该类型的新变体逻辑没有进入实现代码的内容).一些现有的用法将此数据成员专门化为枚举,并且流代码支持将此值来回转换为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");
Run Code Online (Sandbox Code Playgroud)
但是,当我尝试编译它时(在最新更新时使用Visual Studio 2017),我遇到了错误文本
'TestedType': only enumeration type is allowed as an argument to compiler intrinsic type trait '__underlying_type'.看到这个,我尝试了使用std :: disjunction的替代公式,我认为如果早期条件的计算结果为true,我应该对模板进行短路评估(我已经省略了std限定符以使其更具可读性):
disjunction_v<
negation<is_enum<TestedType>>,
is_same<underlying_type_t<TestedType>, int32_t>
>;
Run Code Online (Sandbox Code Playgroud)
我还试图捆绑的违规使用underlying_type_t内部的enable_if上式断言是一个枚举,但没有与任何成功.
我的问题:
布尔运算符一般(std::disjunction特别是)不是模板的短路评估吗?在std :: disjunction的cppreference页面上,它声明了以下内容(强调我的):
断开是短路的:如果模板类型参数Bi与bool(Bi :: value)!= false,则实例化disjunction :: value 不需要实例化Bj :: value for j> i
读到这一点,我原本预计underlying_type_t<TestedType>某些非枚举类型的不良形式是无关紧要的,因为一旦上游的某些内容被评估为真,就不需要考虑下游类型.
如果我在这一点上对规范的读取不正确,是否有其他方法可以在编译时完成此检查,或者我是否需要添加运行时检查来强制执行此操作?
模板参数像常规参数一样急切地"评估".导致问题的部分是underyling_type_t,但它在两个版本中都是完整的.在知道TestedType枚举类型之后,您需要延迟该部分.对于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>();
Run Code Online (Sandbox Code Playgroud)
在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>{});
Run Code Online (Sandbox Code Playgroud)