news 2026/5/23 21:08:59

模板编程——std::is_pointer的分析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
模板编程——std::is_pointer的分析

一、模板元编程库

在C++的标准库中提供了相关的元编程库,提供了一系列的对相关元编程相关的接口,极大的方便了开发者在元编程时的工作。在前面也分析过几个元编程的接口,今天接着分析一下指针处理的元编程接口std::is_pointer。

二、std::is_pointer

标准库中对其的定义为:

template<class T>structis_pointer;

说明也写得非常清楚,它只能检查T是不是一个对象或函数的指针,还特别注明了包括void指针但不包括成员指针。它提供了对cv限定符的支持,其可能实现的源码如下:

template<class T>structis_pointer:std::false_type{};template<class T>structis_pointer<T*>:std::true_type{};template<class T>structis_pointer<T*const>:std::true_type{};template<class T>structis_pointer<T*volatile>:std::true_type{};template<class T>structis_pointer<T*constvolatile>:std::true_type{};

std::true_type这哥俩用到的地方还是挺频繁的。这段代码没啥可解析的,在不同的库中,实现有些大同小异,但基本都差不多。再看一个cppreference的例子:

#include<type_traits>intmain(){structA{intm;voidf(){}};intA::*mem_data_ptr=&A::m;// a pointer to member datavoid(A::*mem_fun_ptr)()=&A::f;// a pointer to member functionstatic_assert(!std::is_pointer<A>::value&&!std::is_pointer_v<A>// same thing as above, but in C++17!&&!std::is_pointer<A>()// same as above, using inherited operator bool&&!std::is_pointer<A>{}// ditto&&!std::is_pointer<A>()()// same as above, using inherited operator()&&!std::is_pointer<A>{}()// ditto&&std::is_pointer_v<A*>&&std::is_pointer_v<Aconst*volatile>&&!std::is_pointer_v<A&>&&!std::is_pointer_v<decltype(mem_data_ptr)>&&!std::is_pointer_v<decltype(mem_fun_ptr)>&&std::is_pointer_v<void*>&&!std::is_pointer_v<int>&&std::is_pointer_v<int*>&&std::is_pointer_v<int**>&&!std::is_pointer_v<int[10]>&&!std::is_pointer_v<std::nullptr_t>&&std::is_pointer_v<void(*)()>);}

是不是觉得代码很easy,确实如此。但越是这样越是要小心。

三、问题和分析

将上面的代码改一下,直接传递进去一个多级指针会是什么样呢?比如下面这样:

class Demo{};intmain(){static_assert(std::is_pointer<Demo*>::value,"Demo is not a pointer type");static_assert(std::is_pointer<Demo**>::value,"Demo is not a pointer type");return0;}

这种情况顺利的通过了编译。表面上看上去没问题,如果只是单纯的处理指针可能觉得也没有什么问题啊。但实际上,如果实际的目的操作的是一个“*Demo”时,前者可以得到一个Demo的对象而后者只能得到一个Demo的指针,这就让后面的行为出现了异化。这对于普通编程来说还好发现,但对于模板编程时,可能就很难定位问题。这就需要进行预防性的处理。
有的开发者可能还没有细看说明,把成员内的指针提供给了这个接口应用,如下面这样:

structS{intfoo(){return0;}};intmain(){static_assert(std::is_pointer<decltype(&S::foo)>::value,"S::foo is not a member pointer");//static_assert(std::is_member_pointer<decltype(&S::foo)>::value, "S::foo is not a member pointer");//OKreturn0;}

这也是不正确的用法。说明中详细了表示了不支持成员指针的处理。所以在元编程的标准库中还提供了一个std::is_member_pointer,用来判断成员的指针属性的情况,也算是完善了std::is_pointer不能判断成员指针的情况。

四、处理方法

那么如何处理is_pointer只能检测最外层指针的情况呢?最简单的方法仍然是采用元编程库提供的std::remove_pointer递归判断。毕竟,几乎没有人写超过三级指针的情况,到二级已经是大多数人的极限了。用大牛的话说,不是说不可以用更多级的指针实现,但这恰恰说明了设计上的问题。扯远了,先看使用std::remove_pointer递归解决二级指针的处理方式:

#include<iostream>#include<type_traits>template<typename T>voidcheckPointerLevel(T ptr){ifconstexpr(std::is_pointer_v<T>){std::cout<<"this is pointer!";// 递归检查指针级别intlevel=0;using curType=T;// remove_pointer处理并检查using BaseType=std::remove_pointer_t<T>;ifconstexpr(std::is_pointer_v<BaseType>){std::cout<<"twice-level pointer!"<<std::endl;}else{std::cout<<"first-level pointer!"<<std::endl;}}else{std::cout<<"not a pointer!"<<std::endl;}}

当然也可以使用SFINAE技术处理:

#include<iostream>#include<type_traits>#include<vector>template<typename T>voidimplCheck(T value,std::true_type){using BaseType=typename std::remove_pointer<T>::type;//业务处理}template<typename T>voidimplCheck(T value,std::false_type){//业务处理}template<class U>voidprocess(U&&t){implCheck(std::forward<U>(t),std::is_pointer<std::remove_reference_t<U>>{});}

其实原理都在前面分析过,此处不再赘述。代码也不复杂,明白几个基础的元编程接口即可。

五、总结

在应用库或者第三方接口时,要认真严格的查看相关的接口说明,一定不能想当然的进行利用。本文这个指针处理的元编程接口,就很容易让初学者上当。这就需要开发者能够全面细致的掌握应用的细节并在适当的例程中进行测试验证。这样才能更好的在工程实践中写出健壮稳定的代码。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/23 13:29:32

亲测好用8个降AIGC网站推荐,千笔帮你轻松降AI率

AI降重工具&#xff1a;让论文更自然&#xff0c;更安心 在如今的学术写作中&#xff0c;AI生成内容虽然提高了效率&#xff0c;但也带来了AIGC率过高的问题。许多学生在提交论文前&#xff0c;都会面临一个共同的难题——如何有效降低AI痕迹&#xff0c;同时保持文章的逻辑性和…

作者头像 李华
网站建设 2026/5/23 14:13:41

java--线程安全问题

概念&#xff1a;Java线程安全问题&#xff0c;本质上是在多线程环境下&#xff0c;由于线程调度的不确定性&#xff0c;导致程序的执行结果与预期不符。 线程安全问题的产生通常可以归结为以下三个核心原因&#xff1a; 原子性问题 (Atomicity) 一个看似简单的操作&#xff0…

作者头像 李华
网站建设 2026/5/9 1:11:02

26.Android系统源码-Brotli 实战 - Google压缩算法核心技术

Brotli 实战 - Google压缩算法核心技术 源码: external/brotli/ (~12,000行 C) 性能: 比Gzip压缩率高20-26%,解压速度相当 设计: Web资源压缩优化,内置字典 标准: RFC 7932 (IETF标准) 1. Brotli vs Gzip/Deflate 压缩算法代际演进: DEFLATE (1993, Gzip/PNG核心):- LZ77 + H…

作者头像 李华
网站建设 2026/5/23 18:59:57

电脑 C 盘满了?这些文件可以安全转移

电脑 C 盘空间告急&#xff0c;不仅会拖慢系统运行速度&#xff0c;还可能导致软件无法安装、文件无法保存。其实很多占用空间的文件都能安全转移到其他盘符&#xff0c;不用重装系统也能轻松释放 C 盘空间。首先可以转移的是下载文件夹。平时下载的安装包、压缩文件、文档图片…

作者头像 李华