【理论实践】指向类模板函数的指针的使用(以std::list为例)

2023-11-15

假设有这个一个场景,我们希望根据条件决定插入元素到list首或尾,条件判断一次,插入操作多次,例如二叉树,至少要处理左和右各一次。

普通的代码很简单,每次操作时,都判断一下,简化一下是一个三元表达式。

巧妙一点的,可以定义一个变量指定接口函数,根据条件设定指定的值,然后后面就可以直接用函数指针了,不再需要重复判断,实现代码如下:

        list<int*> l;
        typedef void (list<int*>::*fun_t)(int* const &);
        bool back = true;
        fun_t f = back ? (fun_t)&list<int*>::push_back : (fun_t)&list<int*>::push_front;        //处理条件
        int* v = nullptr;
        v++;
        (l.*f)(nullptr);        //多次使用
        (l.*f)(v);              //多次使用

几个难点如下,假设相关理论知识已经具备,不解释。

1、类成员函数指针的使用

2、函数重载处理

3、模板类型为指针时注意事项

4、左值类型和右值类型


下文一改以往的思路,带着大家从最容易出错的起点上,一步一步改正确。部分确实是自己命中的,部分是自己推测大部分人会命中的。

1、直观上的写法

        list<int> l;
        auto f = &list<int>::push_back;   //编译错误
        int v = 1;
        (l.*f)(0);      //注意不能写成  l.*f(1);
        (l.*f)(v);
        return 0;
错误:error: unable to deduce ‘auto’ from ‘& std::list<_Tp, _Alloc>::push_back<int, std::allocator<int> >’

原因是push_back重载了,无法自动匹配类型,看一下push_back的定义如下

void push_back (const value_type& val);   //适用于左值和右值
void push_back (value_type&& val);          //仅适用于右值,移动

解决函数重载的办法是定义好对应参数的类型,使只唯一匹配重载的一个函数。

2、第二版代码

        list<int> l;
        typedef void (list<int>::*fun_t)(const int&);  //定义类型
        fun_t f = &list<int>::push_back;
        int v = 1;
        (l.*f)(0);      //注意不能写成  l.*f(1);
        (l.*f)(v);
如果使用重载的另一个函数,则代码如下,

        list<int> l;
        typedef void (list<int>::*fun_t)(int&&);
        fun_t f = &list<int>::push_back;
        int v = 1;
        (l.*f)(0);      //注意不能写成  l.*f(1);
        (l.*f)(v);      //编译报错,   (l.*f)(move(v));正确
报错:error: cannot bind ‘int’ lvalue to ‘int&&’

函数接受一个右值,但传递的是左值,也就是函数要求传递的数据是值,不是变量本身。解决办法是使用std::move


3、似乎一切顺利,麻烦来了

将list类型改为指针int*,以下这个是没有问题的

        list<int*> l;
        typedef void (list<int*>::*fun_t)(int*&&);
        fun_t f = &list<int*>::push_back;
        int* v = nullptr;
        v++;
        (l.*f)(nullptr);
        (l.*f)(move(v));

另一个报错

        list<int*> l;
        typedef void (list<int*>::*fun_t)(const int*&);
        fun_t f = &list<int*>::push_back;  //报错
        int* v = nullptr;
        v++;
        (l.*f)(nullptr);
        (l.*f)(move(v));

报错如下

/usr/include/c++/4.8.2/bits/stl_list.h:1020:7: note: candidates are: void std::list<_Tp, _Alloc>::push_back(std::list<_Tp, _Alloc>::value_type&&) [with _Tp = int*; _Alloc = std::allocator<int*>; std::list<_Tp, _Alloc>::value_type = int*]
       push_back(value_type&& __x)
       ^
/usr/include/c++/4.8.2/bits/stl_list.h:1015:7: note:                 void std::list<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = int*; _Alloc = std::allocator<int*>; std::list<_Tp, _Alloc>::value_type = int*]
       push_back(const value_type& __x)

意思是类型不匹配,有两个重载定义,一个是push_back(type&&),另一个是push_back(const type&),type是 int*


解释之前,先给出一下正确代码

        list<int*> l;
        typedef void (list<int*>::*fun_t)(int* const &);  //注意一个const位置
        fun_t f = &list<int*>::push_back;
        int* v = nullptr;
        v++;
        (l.*f)(nullptr);
        (l.*f)(v);


原因:模板参数const永远是修饰的是形参而不是类型,所以指针类型时, const TYPE p 实际是 TYPE const p。


4、一切准备妥当,开始写逻辑吧,先来个错误的版本

        list<int*> l;
        typedef void (list<int*>::*fun_t)(int* const &);
        bool back = true;
        fun_t f = back ? &list<int*>::push_back : &list<int*>::push_front;        //编译错误
        int* v = nullptr;
        v++;
        (l.*f)(nullptr);        //使用
        (l.*f)(v);              //使用

错误信息:error: address of overloaded function with no contextual type information

原因很简单,三元表达式先计算值,然后赋值给变量。在计算值时,并不知道会赋值给谁,对于重载,无法选取对应类型的,解决办法就是取地址时做一下转换。


5、完结,正确代码见开头。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【理论实践】指向类模板函数的指针的使用(以std::list为例) 的相关文章

随机推荐

  • VScode tab缩进太小 空格长度太小问题(Ubuntu)

    一 问题描述 Ubuntu18 04 安装vscode后 发现tab的缩进太小 初以为是tab的空格数没设置对 经确认后 没问题 一个tab是四个空格 下面两图分别是异常和正常的缩进显示 二 原因分析 在我的另一台虚拟机 ubuntu16
  • Google App Engine对Java支持情况一览

    Google App Engine对Java支持情况一览 http developer 51cto com 2009 04 09 11 06 杨赛 译 51CTO com 我要评论 0 Google App Engine的Java支持已发布
  • 华为OD机试-快递运输

    Online C compiler to run C program online include
  • Java程序的三种流程结构

    1 顺序结构 按照顺序一句一句执行 Java的基本结构就是顺序结构 2 循环结构 在顺序结构的基础下 重复执行相同或者相似的代码 for while do while 3分支结构 有条件的去执行某个语句 条件满足就执行下面的语句 条件不满足
  • SQL注入攻击流程

    1 判断SQL注入点 本质原理是 找一个需要后台处理后提交给数据库的点 所有的输入只要和数据库进行交互的 都有可能触发SQL注入 一般为三大类 Get参数触发SQL注入 POST参数触发SQL注入 Cookie触发SQL注入 而验证是否存在
  • 华为手机打开图片很慢是怎么回事_华为手机相册打开很慢怎么解决?

    造成卡顿的原因 相册里存储信息较多 由于手机读取相册中的信息需要一定时间 可能出现卡顿现象 系统卡顿通用解决办法 请保持手机电量高于 20 手机低电量时为了延长待机时间 保护手机 会对手机的性能进行限制 卸载第三方手机管家类软件 如果您的手
  • 【Python】首届一年一度秀代码时间罒ω罒

    声明 以下代码大家如果有兴趣的话可以用LDLE代码编辑器运行看看 NO 1 万能计算器 难度系数 1 算术运算符 a input 请输入第一个数字 b input 请输入第二个数字 a float a b float b print 和 a
  • 小韭菜

    大家好 我是章鱼猫 今天给大家推荐的这个项目是 leeks 原名小韭菜 后改名为 leeks 这是一个 IDEA 查看股票 基金插件 写代码的同时还能瞄一眼股票 基金 真的是工作 搞基两不误 安装 下载当前最新的安装包 leeks 1 3
  • 解决 linux在 VMware中的问题 汇总 (***)

    目录 解决Ubuntu在VMware中不能全屏的问题 无需安装 VMware tools 解决虚拟机与宿主机的文件共享问题 Ubuntu Window下 X2Go 安装 连接 同步 上传文件夹 复制 粘贴 桌面共享 Linux挂载共享文件夹
  • 命令行中Gradle创建项目的打包和运行,新手起步

    第一步 下载 下载 https gradle org 官网 gt install gt releases page gt binary only 下载 下载页 https gradle org releases close notifica
  • vue中在字符串中添加点击事件

    如果你在用vue写项目中遇到了需要在字符串中写点击事件的奇葩写法 你会不会感到很头疼 我不知道你会不会 反正我肯定会 没办法谁叫咋遇到了呢 话不多说 直接上代码 html div class ml 12 mr 12 mt 8 div 采用的
  • 华为OD机试 - 可以组成网络的服务器(Java)

    题目描述 在一个机房中 服务器的位置标识在 n m 的整数矩阵网格中 1 表示单元格上有服务器 0 表示没有 如果两台服务器位于同一行或者同一列中紧邻的位置 则认为它们之间可以组成一个局域网 请你统计机房中最大的局域网包含的服务器个数 输入
  • Docker-compose安装及使用教程

    docker compose安装 方法一 首先执行pip V确认是否已安装pip 若提示 未找到命令 则根据以下步骤执行 若显示pip版本则直接执行步骤2安装即可 1 安装pip wget https bootstrap pypa io g
  • SpringBoot 中定时执行注解(@Scheduled、@EnableScheduling)

    项目开发中经常需要执行一些定时任务 比如需要在每天凌晨时候 分析一次前一天的日志信息 Spring为我们提供了异步执行任务调度的方式 提供TaskExecutor TaskScheduler 接口 SpringBoot中使用两个注解 Ena
  • C++中关于隐藏的理解

    引言 在使用中弄清楚隐藏的区别之后 还需要明白怎么使用 下面说以下隐藏 重写 重载的区别 与重载的区别 在父类与子类中 函数名相同 参数不同 无论父类中的同名函数是否含有virtual关键字 都是隐藏 与重写的区别 在父类和子类中 函数名相
  • MySQL引起索引失效的原因

    原创92 4 发布于2018 11 30 19 35 07 阅读数 451 收藏 展开 查看索引结构 mysql gt show index from staffs Table Non unique Key name Seq in inde
  • cmake_policy规定了cmake解析行为

    https blog csdn net gispipi article details 108275569
  • Python中字符串和列表去重方法

    本文主要为大家整理了Python中实现字符串和列表去重的常用方法 文中的示例代码讲解详细 对我们深入了解Python有一定的帮助 感兴趣的可以了解一下 字符串去重 1 直接遍历字符串的方式 1 2 3 4 5 6 7 8 coding UT
  • Redis第三讲 Redis 4.0 混合持久化与Redis数据备份策略

    RDB 和 AOF 持久化各有利弊 RDB 可能会导致一定时间内的数据丢失 而 AOF 由于文件较大则会影响 Redis 的启动速度 为了能同时使用 RDB 和 AOF 各种的优点 Redis 4 0 之后新增了混合持久化的方式 加载优先级
  • 【理论实践】指向类模板函数的指针的使用(以std::list为例)

    假设有这个一个场景 我们希望根据条件决定插入元素到list首或尾 条件判断一次 插入操作多次 例如二叉树 至少要处理左和右各一次 普通的代码很简单 每次操作时 都判断一下 简化一下是一个三元表达式 巧妙一点的 可以定义一个变量指定接口函数