当前位置: 首页 > article >正文

模版的特化引发的权限扩大的解决方法

一:引发权限扩大的场景

假设现在有一个仿函数,用来替代 <小于 的比较功能,如下

template<class T>
	class less
	{
		bool operator()(const T& x, const T& y) 
		{
			return x < y;
		}
	};

现在我们传两个int,或者两个double,就能调用在另一个类中调用仿函数

新增需求:能比较两个int*指向的内容的大小,且不能舍弃比较普通类型的大小的功能

所以:既要比较int*指向的内容的大小,也要能比较两个int的大小,那模版的特化就能轻松的解决

特化代码:

template<class T>
	class less
	{
		bool operator()(const T& x, const T& y) 
		{
			return x < y;
		}
	};

//模版的特化 针对指针类型
template<>
	class less<T*>
	{
		bool operator()(const T*& x, const T*& y) 
		{
			return *x < *y;
		}
	};

错误思维:既然是针对指针的特化 ,那我们特化的代码中的 T全部替换成T*应该就可以了吧

此时:当我们想当然的传递两个int*除法仿函数的特化的时候,会产生以下报错

为什么会产生权限扩大的报错呢??

二:错误的本质

①:权限问题

解释:C/C++中,权限缩小和平移视为合理的,而权限扩大会报错

a:a是一个const修饰int类型的变量(可读不可写),不能被一个正常的int类型的变量b所引用(可读可写),这是权限的扩大

b: c是一个int类型的变量(可读可写),能被一个正常的int类型的变量d所引用(可读可写),这是权限的平移

c:e是一个int类型的变量(可读可写),能被一个const修饰的int类型的变量f所引用(可读不可写),这是权限的缩小

好比:你是一个村长,你可以管理村里事务,也可以不管理,但你不可能管理县里的事务

总结:权限放大会报错,缩小平移合理

注意:

解释:这是对的,是合理的!

    会有人认为e是cosnt的,而f是正常的,权限放大了每一个报错

 int f = e; 并没有涉及权限,而是直接进行了值拷贝(即 e 的值 30 被直接赋给了 f),只是简单地把 e 的值(30拷贝到 f,并保证 f 不能被修改。

②:类型转换会生成具有常性的临时变量

解释:为什么double前面加上了const就行了?

因为 int 到 double 是不同类型,会触发类型转换,而类型转换会生成一个具有常属性(可读不可写)的临时变量,此时b和d都是对这个常属性临时变量进行引用,而按照①权限问题可以,权限只能缩小和平移,所以既然你这个临时变量是只可读不可写,那我只能用const修饰的变量d来就接收你,因为const修饰的变量也是只可读不可写

总结:类型转换时,左操作数应该被const修饰

Q:你怎么知道类型转换会生成一个临时变量?

A:下图可解释

解释:b得到了a的临时变量转换而来的10,所以a本身还是一个double的值,不受影响

③:const修饰的类型和普通类型 是两种类型 可能会涉及类类型转换

在 C++ 中,const int 和 int 是不同的类型,但通常它们之间的赋值或初始化是隐式兼容的(因为 const 只是增加限制,而不是改变底层类型)。不过,在涉及 引用、指针或模板 时,(也就是博客中所展示的这种场景的时候)它们的区别会更明显。我们可以通过以下代码验证它们确实是不同的类型,并在某些情况下触发类型转换:

1. 模板类型推导(const int 和 int 是不同的类型)

#include <iostream>
#include <type_traits>

template<typename T>
void checkType() {
    if (std::is_same_v<T, int>) {
        std::cout << "Type is int" << std::endl;
    }
    else if (std::is_same_v<T, const int>) {
        std::cout << "Type is const int" << std::endl;
    }
    else {
        std::cout << "Other type" << std::endl;
    }
}

int main() {
    int a = 10;
    const int b = 20;

    checkType<decltype(a)>();  // 输出:Type is int
    checkType<decltype(b)>();  // 输出:Type is const int

    return 0;
}

输出:

Type is int
Type is const int

总结:
int 和 const int 在模板推导时被视为不同的类型。 

 

④:对特化参数的解读

Q: const T*& x中的const修饰的是什么? 假设为const int*& x

A:const修饰的 *x ,表示指针指向的内容不能被修改,也就是说int* 是被const修饰的

问题来了:

我们调用仿函数的时候,传参是两个int*的时候,我们想当然的去比较指针指向的内容。

此时参数是一个被const修饰的int*,而实参是int*,这是两种类型,会发生类型转换,按照前文所言,会生成一个具有常属性(可读不可写)的临时变量,然后const T*& x 中的 &,作用是让x去引用这个临时变量,而x呢,没被const修饰,直接去引用一个常属性的临时变量,权限扩大,则报错!!

解决方法:

让x也被const修饰就行了,所以参数变为const T*cosnt & x 即可,第一个const修饰*x(指针指向的内容),第二个const修饰x,以让x能够正确的引用一个常属性的临时变量

图解:

总结:偏特化的时候 对于指针不建议加引用 

指针不加引用也没啥,指针大小最大8字节,而加了引用,则需要双const

三:易混淆点

我们权限扩大中的例子中的权限缩小的例子,会触发类型转换吗,生成临时变量吗?

int e = 30;
const int& f = e;  // ✅ 正确:隐式生成临时变量(但优化后可能直接绑定)

解释:

优化:现代编译器通常会直接让 f 引用 e(因为 e 本身是可修改的,const 只是限制通过 f 修改 e),不会真正生成临时变量


 


http://www.kler.cn/a/599274.html

相关文章:

  • 基于51单片机的双机通信温度检测报警系统的仿真设计
  • 腾讯云大模型知识引擎×DeepSeek | 企业应用快速接入手册
  • LVS-DR模式配置脚本
  • 5.4 位运算专题:LeetCode 137. 只出现一次的数字 II
  • 模糊推理规则生成方法详解
  • CentOS8 安装 Docker-CE
  • FPGA中串行执行方式之流水线(Pipeline)
  • Spring MVC配置详解:从历史到实战
  • Node.js系列(6)--安全实践指南
  • 基于PySide6与pycatia的CATIA绘图文本批量处理工具开发实践
  • 永久禁用 firewalld: systemctl disable firewalld
  • C++类与对象的第二个简单的实战练习-3.24笔记
  • MobaXterm配置ssh端口转发autodl服务器网页页面
  • UNIX网络编程笔记:TCP、UDP、SCTP编程的区别
  • 机器视觉工程师如何看机器视觉展会,有些机器视觉兄弟参加机器视觉展会,真的是参加了?重在参与?
  • 【机器学习/大模型/八股文 面经 (一)】
  • 如何扩展 Linux 中 ext4 文件系统的大小
  • 补Java基础之重生(13)类与对象(补充版)+面向对象综合案例
  • 智算中心系统化建设与运营框架
  • Netty源码—5.Pipeline和Handler一