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

C++标准模板库学习--函数模板返回值参数类型

template<typename T1, typename T2>
2 T1 max (T1 a, T2 b)
3 {
4 return b < a ? a : b;
5 }
6 ...
7 auto m = ::max(4, 7.2); // OK, 不过返回类型与第一个参数类型一样

如何解决模板的返回类型

法一,使用decltype进行类型推断,在编译时确定返回值类型,就能根据实例化时参数类型确定了。

template<typename T1, typename T2>
auto max1(T1 a, T2 b) -> decltype(a < b ? b : a)
{
	return a < b ? b : a;
}

法二,直接使用C++14的特性auto,进行类型自动推导

编译器可以自动推断函数的返回类型,无需使用尾随返回类型。

template<typename T1, typename T2>
auto max2(T1 a, T2 b)
{
	return a < b ? b : a;
}

法三,使用模板元编程工具——公共类型std::common_type

介绍common_type

std::common_type 是 C++ 标准库 <type_traits> 头文件中提供的一个模板元编程工具,用于确定一组类型的公共类型。它的主要作用是在编译时找出能够表示所有给定类型值的最通用类型。

在处理模板编程时,经常会遇到需要对不同类型进行操作并返回一个合适类型的情况,std::common_type 就可以帮助我们自动推断出这个合适的类型。例如,当你有一个模板函数,需要对不同类型的参数进行比较或运算,然后返回一个合适的结果类型,std::common_type 就非常有用。

如何使用

1,获取公共类型
	using common_type = std::common_type_t<int, double>;
	std::cout << "common_type of int and double is: " << typeid(common_type).name() << std::endl;

2,确定合适的函数返回类型

template<typename T1,typename T2>
typename std::common_type<T1, T2>::type max3(T1 a, T2 b)
{
	return a < b ? b : a;
}

int main()
{
	using common_type = std::common_type_t<int, double>;
	std::cout << "common_type of int and double is: " << typeid(common_type).name() << std::endl;
	std::cout << max1(4, 7.2) << std::endl;
	std::cout << max2(4, 7.2) << std::endl;
	std::cout << max3(4, 7.2) << std::endl;
	return 0;
}

关键:如何自己去实现common_type 和其简化common_type_t

1,先学std::add_rvalue_reference

std::add_rvalue_reference 是 C++ 标准库 <type_traits> 头文件中的一个模板元编程工具,属于类型特征(Type Traits)库的一部分。它的主要作用是在编译时为给定的类型添加右值引用(&&)。下面从定义、使用方法、实现原理等方面详细介绍。

定义和作用

std::add_rvalue_reference 是一个模板结构体,它接受一个类型参数 T,并通过 ::type 成员来提供添加右值引用后的类型。其主要用途是在模板编程中根据需要修改类型,特别是在进行类型推导和泛型编程时,确保类型具有合适的引用属性。

使用方法
int main()
{
	using Rvalueint = std::add_rvalue_reference<int>::type;
	using Rvalueint2 = std::add_rvalue_reference<int&>::type;

	std::cout<<std::boolalpha;

	cout<<"Is Rvalueint a reference? "<<std::is_reference<Rvalueint>::value<<endl;
	cout<<"Is Rvalueint2 a reference? "<<std::is_reference<Rvalueint2>::value<<endl;

	using Rvalueint3 = std::add_rvalue_reference<int&&>::type;
	cout<<"Is Rvalueint3 a reference? "<<std::is_reference<Rvalueint3>::value<<endl;
	return 0;
}

用来生成引用类型,如果已经是引用类型,类型不变。

具体实现
//the add_rvalue_reference trait

//the basic definition
template<typename T>
struct my_add_rvalue_reference
{
	using type = T&&;
};

//Specialization: When T is already an rvalue reference, keep it unchanged.
template<typename T>
struct my_add_rvalue_reference<T&&>
{
	using type = T&&;
};

//Alias template for simplifying type access
template<typename T>
using my_add_rvalue_reference_t = typename my_add_rvalue_reference<T>::type;

2,再了解std::declval

std::declval 是 C++ 标准库中的一个工具,用于在未求值语境中获得对象的引用,主要配合 decltype 进行编译时类型推导。它不会在运行时被调用,只是为了帮助编译器分析类型。下面从标准库中的声明、实现原理、模拟实现等方面来详细介绍。

标准库中的声明

在 C++ 标准库中,std::declval 通常在 <utility> 头文件中声明,其声明形式如下

template< class T >
typename std::add_rvalue_reference<T>::type declval() noexcept;
实现原理

std::declval 实际上并没有真正的函数定义,它只有声明。因为它的目的是在编译时进行类型推导,并不需要在运行时执行具体的代码逻辑。如果在运行时尝试调用 std::declval,会导致链接错误,因为找不到对应的函数定义。编译器会在编译过程中处理 std::declval 的使用,利用它来辅助进行类型分析。

为什么没有函数的定义?
设计目的决定

std::declval 的设计目的是在编译时辅助进行类型推导,而不是在运行时执行具体的操作。它主要用于在不实际构造对象的情况下,让编译器分析对象的类型和相关表达式。如果为其提供函数定义,就意味着在运行时需要执行该函数,这与它的设计初衷相悖。

避免副作用

如果 std::declval 有函数定义,在调用时就会涉及对象的构造、析构等操作,可能会产生不必要的副作用。例如,对于一些构造函数有复杂逻辑或者构造函数被删除的类型,调用构造函数可能会导致编译错误或者意外的行为。通过只声明不定义,std::declval 避免了这些问题,确保只在编译时用于类型推导。

链接错误作为保护机制

由于 std::declval 没有定义,在运行时调用它会导致链接错误。这实际上是一种保护机制,提醒开发者 std::declval 只能用于编译时的类型分析,不能在运行时使用。

declval用于未求值语境(未求值语境(unevaluated context)是指在编译过程中,某些表达式只用于类型检查和推导,而不会被实际求值的情况。在未求值语境中,表达式不会产生运行时的效果,编译器只会分析其类型信息。)获取传入参数类型。

3,comon_type具体实现

template<typename T1,typename T2>
using Common_type = decltype(true ? declval<T1>() : declval<T2>());

解释为什么使用到三目运算和declval

在 C++ 中,三元运算符(三目运算符)?: 有一套明确的类型转换规则。当使用 true ? expr1 : expr2 时,编译器会根据 expr1 和 expr2 的类型来确定整个表达式的类型。具体规则如下:

  • 如果 expr1 和 expr2 类型相同,那么整个表达式的类型就是该类型。
  • 如果 expr1 和 expr2 类型不同,编译器会尝试进行隐式类型转换,找到一个能够同时表示 expr1 和 expr2 值的公共类型。例如,对于 int 和 double 类型,由于 double 可以表示 int 类型的值,编译器会将 int 类型的值转换为 double 类型,因此整个表达式的类型就是 double

std::declval 用于在不实际构造对象的情况下获取对象的引用,而 decltype 用于在编译时推断表达式的类型。通过将 std::declval<T1>() 和 std::declval<T2>() 作为三元运算符的两个操作数,我们可以利用三元运算符的类型转换规则,让编译器在编译时推断出 T1 和 T2 的公共类型。

使用如下

	using Type = Common_type<int, double>;
	std::cout << "Type is " << typeid(Type).name() << std::endl;

相关文章:

  • Linux驱动开发之中断处理
  • 网页转图片的方法(超出可视范围的也可以)dom-to-image
  • 网编高级 day03
  • dify-1.0.1 + deepseek调用本地知识库
  • ASP4644四通道降压稳压器的工业高效电源管理方案
  • linux常用基本指令汇总
  • vue3:八、登录界面实现-忘记密码
  • Dump 文件介绍
  • Symmetry Protected Topological phases of Quantum Matter——对称性保护量子物质的拓扑相位
  • 2.PPP专题
  • SAP IBP for Supply Chain Certification Guide (Parag Bakde, Rishabh Gupta)
  • 【推荐项目】049-物流系统技术管理平台
  • 实验篇| CentOS 7 下 Keepalived + Nginx 实现双机高可用
  • 电气制作行业
  • Vim软件使用技巧
  • 前端登录鉴权全解析:主流方案对比与实现指南
  • 996引擎-自定义属性:配表 + 映射
  • 某大厂自动化工程师面试题
  • FFMPEG录制远程监控摄像头MP4
  • 量子纠缠与类空间隔信息传送
  • 舞者王佳俊谈“与AI共舞”:像多了一个舞伴,要考虑它的“感受”
  • 国际乒联主席索林:洛杉矶奥运会增设混团是里程碑事件
  • 3月中国减持189亿美元美债、持仓规模降至第三,英国升至第二
  • 梅花奖在上海|湘剧《夫人如见》竞梅,长沙文旅来沪推广
  • 马上评|家长抱婴儿值护学岗,如何避免“被自愿”?
  • 中国证券业协会修订发布《证券纠纷调解规则》