为什么类型特征不适用于命名空间范围内的类型?
Why do type traits not work with types in namespace scope?
我正在为我的C++序列化库设计类型寄存器功能。 但是我遇到了一个关于类型特征的奇怪问题。
我正在使用Visual Studio 2017和/std:c ++ latest。
#include <type_traits>
int reg(...);
template<class T>
constexpr bool is_known = !std::is_same_v<decltype(reg((T*)1)), int>;
//----- for type1 in global scope ------
struct type1 {};
void reg(type1 *);
static_assert(is_known<type1>); // success
//----- for type2 in namespace scope ----
namespace ns { struct type2 { }; }
void reg(ns::type2 *);
static_assert(is_known<ns::type2>); // fail!!!!
对于全局作用域中的类型 1,静态断言成功,但对于命名空间作用域类型2,静态断言失败。
为什么会有区别?
在查找reg((T*))
以查找所引用的reg
时,会检查两组位置。 第一个是声明模板的位置(int reg(...)
可见的位置),第二个是 ADL 在模板首次使用新类型实例化的位置。
ns::type2*
上的 ADL(参数相关查找)不检查全局命名空间。 它检查与该类型关联的命名空间,即在本例中为ns
。 ADL 不会检查"围绕"或"高于"关联命名空间的命名空间。
::type1
的 ADL 会检查全局命名空间。
模板不是宏。 它们的行为就像您在实例化生成的代码时复制粘贴了它一样。MSVC 过去将模板视为宏,但它们越来越符合标准。 他们为其合规性工作指定的名称是"两阶段名称查找",如果您想跟踪它在特定版本中中断的原因。
解决方法是将reg
移动到ns::type2
的命名空间中,或者确保您在其中定义reg
的命名空间与要reg
的参数相关联(例如使用标记模板而不是指针),或者在定义其在decltype
中的用法之前定义reg
。 或者更花哨的东西;没有潜在的问题描述,我无法猜测。
TLDR 该机制被称为两阶段查找,其规则是晦涩难懂的。经验法则是始终在与它用于避免恶作剧的类型相同的命名空间中声明函数。
当存在依赖名称时,会发生 2 阶段查找,此时名称查找将延迟到实例化点。如果名称为非限定,则查找的结果是在定义点的非限定查找和实例化点的参数相关查找的并集。
这到底意味着什么?
依赖名称
如果名称(例如函数名称)的含义取决于模板参数,则该名称(例如函数名称)是相关的。在您的情况下,reg
取决于T
,因为参数类型T*
取决于T
。
实例化点
模板别名不是类型,它们表示整个类型族。当您为其提供参数时,该类型被称为从模板实例化。实例化点是程序中模板别名首次与实际参数一起使用的位置。
非限定名称
如果名称之前没有范围解析运算符,则称其为不合格,例如reg
是不合格的。
不合格的查找
每当一个名称出现在程序中时,都必须找到它的声明,这称为名称查找。非限定查找从名称出现的作用域中查找名称,并按顺序向外搜索。
依赖于参数的查找
也称为 ADL,这是另一个查找规则,当要查找的函数名称不限定并且函数的参数之一是用户定义的类型时,它适用。它在类型的关联命名空间中查找名称。关联的命名空间包括定义类型的命名空间等。
总之,由于is_known
是在以下reg
重载之前定义的,非限定查找只能找到reg(...)
。由于reg(ns::type2*)
不在ns::type2
的相关命名空间中,因此ADL也找不到它。
- 为什么在全局范围内使用"extern int a"似乎不行?
- 错误:未在此范围内声明'reverse'
- 并行用于C++17中数组索引范围内的循环
- 求出有多少个数字是完美平方,而sqrt()是L,R范围内的素数
- 如何修复错误,迭代器未在此范围内声明,并且迭代器未命名类型'
- 在 c++ 中确定某个值是否在该数据类型的最大范围内
- 为什么类型特征不适用于命名空间范围内的类型?
- 模板化函数范围内的条件模板化类型别名
- 更改if范围内变量的类型
- OpenCV-浮动数据类型和强度范围在0-255范围内的RGB通道
- 类模板实例化错误:未在此范围内声明类型
- 说明 资源路径位置类型 'UINT16_MAX' 未在此范围内声明
- 3 错误:错误:未在此范围内声明'Entry'。错误:模板参数 1 无效。错误:令牌之前声明中的类型无效'('
- 检查长类型数据是否在 int 范围内的便携式方法是什么?
- 如何检查两个数字是否在浮点类型的精度限制的有效数字"x"范围内?
- 在类范围内获取虚方法的类型
- 检查输入是否为特定类型并在特定范围内
- 在整数类型的整个范围内进行迭代
- 通过模板为项目范围内的唯一id自动增加类型
- 在编译时检查指定的值是否在类型的范围内