C ++ 11提供了standard <type_traits>
。
没有编译器挂钩,它们中的哪一个是不可能实现的?
__is_builtin...
。我在这里写下了一个完整的答案-这是一个正在进行的工作,因此即使我将文本剪切并粘贴到此答案中,我也提供了权威的超链接。
另请参见libc ++有关类型特征固有设计的文档。
is_union
查询未通过任何其他方式公开的类的属性;在C ++中,您可以使用类或结构进行的任何操作,也可以使用联合进行的操作。这包括继承和获取成员指针。
这些特征查询未通过任何其他方式公开的类的属性。本质上,结构或类是“黑匣子”。C ++语言使我们无法破解它并检查其数据成员,以查明它们是否均为POD类型,或者是否为POD类型,或者private
该类是否具有任何constexpr构造函数(的关键要求is_literal_type
)。
is_abstract
是一个有趣的案例。抽象类类型的定义特征是您无法获得该类型的值;因此,例如,定义一个参数或返回类型为抽象的函数的格式不正确,而创建一个其元素类型为抽象的数组类型的格式不正确。(奇怪的是,如果T
是抽象的,那么SFINAE将适用于T[]
但不适用T()
。也就是说,用抽象的返回类型创建函数的类型是可以接受的;定义这种函数类型的实体格式不正确。 )
因此,我们可以非常接近is_abstract
使用这种SFINAE方法的正确实现:
template<class T, class> struct is_abstract_impl : true_type {};
template<class T> struct is_abstract_impl<T, void_t<T[]>> : false_type {};
template<class T> struct is_abstract : is_abstract_impl<remove_cv_t<T>, void> {};
但是,有一个缺陷!如果T
本身是模板类,例如vector<T>
或basic_ostream<char>
,则仅形成类型T[]
是可以接受的;在未评估的上下文中,这不会导致编译器实例化其主体T
,因此,编译器将不会检测到数组类型的格式错误T[]
。因此,在那种情况下SFINAE不会发生,我们将为给出错误的答案is_abstract<basic_ostream<char>>
。
在未评估的上下文中模板实例化的怪癖是现代编译器提供的唯一原因__is_abstract(T)
。
is_final
查询未通过任何其他方式公开的类的属性。具体来说,派生类的基本说明符列表不是SFINAE上下文。我们不能利用这种enable_if_t
方式问“我可以创建一个派生自该类的类T
吗?” 因为如果我们不能创建这样的类,那将是一个很难的错误。
is_empty
是一个有趣的案例。我们不能仅仅问是否sizeof (T) == 0
因为在C ++中不允许类型的大小为0?即使是空的班级也有sizeof (T) == 1
。但是,由于“空基数优化”,“空性”足够重要以值得拥有类型特征:所有足够现代的编译器都会布局这两个类
struct Derived : public T { int x; };
struct Underived { int x; };
同样 也就是说,它们不会Derived
为空的T
子对象留出任何空间。这建议了一种方法,至少在所有足够现代的编译器上,我们都可以在C ++ 03中测试“空性”:只需定义上述两个类,然后询问是否为sizeof (Derived) == sizeof (Underived)
。不幸的是,从C ++ 11开始,此技巧不再起作用,因为它T
可能是最终的,并且任何其他方式都不会暴露类类型的“最终性”!因此,实现标准的编译器供应商final
还必须公开类似内容__is_empty(T)
。
is_enum
是另一个有趣的案例。从技术上说,我们可以通过观察实现这种类型的特征,如果我们的类型T
是不是一个根本的类型,数组类型,指针类型,引用类型,成员指针,类或联合,或功能型,然后通过流程消除它必须是枚举类型。但是,如果编译器碰巧支持不属于上述类别的任何其他类型,则这种演绎推理将失效。因此,现代编译器公开了__is_enum(T)
。
不属于以上任何类别的受支持类型的常见示例是__int128_t
。libc ++实际上会检测到它的存在__int128_t
并将其包括在“积分类型”的类别中(这在上述分类中使其成为“基本类型”),但是我们的简单实现并未实现。
另一个示例是vector int
在支持Altivec矢量扩展的编译器上。这种类型显然更是“不是整数”,但也“不是其他任何东西”,并且最肯定不是枚举类型!
构造,赋值和破坏的琐碎性是该类的所有属性,这些属性不会通过任何其他方式公开。注意,有了这个基础,我们不需要任何额外的魔术来查询默认构造,副本构造,移动分配等的琐碎性。而是is_trivially_copy_constructible<T>
根据来实现的is_trivially_constructible<T, const T&>
,依此类推。
由于历史原因,此内置编译器的名称不是__is_trivially_destructible(T)
,而是__has_trivial_destructor(T)
。此外,事实证明,true
对于具有删除的析构函数的类类型,内建函数的求值甚至是!因此,我们首先需要检查该类型是否可破坏;然后,如果是的话,我们可以问魔术内置函数,该析构函数的确微不足道。
枚举的基本类型不会通过任何其他方式公开。您可以通过sizeof(T)
将其与所有已知类型的大小进行比较并将其与之接近,并通过来查询基础类型的带正负号T(-1) < T(0)
。但是该方法仍然无法区分底层类型int
和long
在这些类型具有相同宽度的平台上(在这些类型具有相同宽度的平台之间long
和long long
之上)。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句