C++ 类型推导(第一部分)

在调用模板(函数)的时候,编译器会根据实参类型推导模板参数类型和函数参数类型,从而实例化出对应的函数。通常模板参数类型的推导结果与函数参数类型的推导结果不一样,这是因为函数参数类型会有诸如 const、reference、volatile 等修饰符。模板参数类型的推导结果 不仅取决于实参类型 还依赖函数参数类型的形式 ,本文只讨论其中两种形式------函数参数类型组成部分包含指针或引用,但不是万能引用、函数参数组成部分既没有指针也没有引用

如下代码片段 T,即模板参数类型;ParamType,为函数参数类型(形式);expr 为实参,其类型是 int。

cpp 复制代码
template<typename T>
void foo(ParamType v) {
}

int expr = 9;
foo(expr);

一、ParamType 是个引用(指针)但不是万能引用时 T 的推导规则

  1. 如果实参(expr)含有引用,先将其引用部分忽略;
  2. 然后把 expr 的类型和 ParamType的类型进行模式匹配,进而确定 T 的类型。

二、ParamType 既非指针也非引用时 T 的推导规则

  1. 如果实参(expr)含有引用,则忽略其引用部分;
  2. 忽略实参引用性之后,若实参是个const对象,就要忽略 const、同样若实参是个 volatile 对象,也要忽略 volatile。

三、推导过程举例

上文提到 ParamType 的形式决定T的推导结果,接下来一一列举 ParamType 的各种形式的推导过程:

  • ParamType 为 T& 形式

可以看到推导结果输出内容显示:T 被推到为 int 或 const int,ParamType 则对应为 int& 或 const int&。当调用者向引用类型的函数参数传入const 对象时,意味着不希望在函数体内修改该对象,所以应该将实参的常量性(constness)成为模板参数T的类型推导结果的组成部分。虽然调用者传入了带引用性(reference-ness)的实参,但是在推导过程中忽略了其引用性(reference-ness),所以模板参数T的类型推导结果中不包含引用部分。

cpp 复制代码
#include <iostream>

#include <boost/type_index.hpp>

template<typename T>
static inline void test_ref_pattern(T& v) {
	using boost::typeindex::type_id_with_cvr;
	using std::cout;
	using std::endl;

	cout << "T=" << type_id_with_cvr<T>().pretty_name()
		<< ", ParamType=" 
        << type_id_with_cvr<decltype(v)>().pretty_name() << endl;
}

int main() {
    int x = 66;
    const int cx = x;
    int& ref = x;
    const int& cref = ref;
    test_ref_pattern(x);
    test_ref_pattern(ref);
    test_ref_pattern(cx);
    test_ref_pattern(cref);
 }
  • ParamType 为 const T& 形式

当函数参数类型添加 const 属性时,模板参数的类型推导结果、函数参数的类型推导结果与上边有所差异:由于函数参数中已经包含常量性(constness),所以模板参数的类型推导结果就没包含 const 必要了。

cpp 复制代码
template<typename T>
void test_const_ref_pattern(const T &v) {
	using boost::typeindex::type_id_with_cvr;
	using std::cout;
	using std::endl;

	cout << "T=" << type_id_with_cvr<T>().pretty_name()
		<< ", ParamType=" 
        << type_id_with_cvr<decltype(v)>().pretty_name() << endl;
}

int main() 
{
    int x = 66;
    const int cx = x;
    int& ref = x;
    const int& cref = ref;

    test_const_ref_pattern(x);
    test_const_ref_pattern(ref);
    test_const_ref_pattern(cx);
    test_const_ref_pattern(cref);
}
  • ParamType 为 T 形式

这种形式下意味着实参按照"值传递"。这样即使实参有 const 属性,形参仍可不具备 const 属性,换句话说实参不能被修改并不能说明形参在函数体内不能被修改。所以实参的常量性(constness)和挥发性(volatileness)可以在推导过程中忽略。需要重点说明的是去除这两种属性仅发生在"值形参"模式时。

cpp 复制代码
template<typename T>
void test_value_pattern(T v) {
	using boost::typeindex::type_id_with_cvr;
	using std::cout;
	using std::endl;

	cout << "T=" << type_id_with_cvr<T>().pretty_name()
		<< ", ParamType=" 
        << type_id_with_cvr<decltype(v)>().pretty_name() << endl;
}

int main() 
{
    int x = 66;
    const int cx = x;
    int& ref = x;
    const int& cref = ref;

    test_value_pattern(x);
    test_value_pattern(ref);
    test_value_pattern(cx);
    test_value_pattern(cref);
}
  • 传递具有顶层const和底层const的实参传递

这种情况类型推导过程中p所指对象的常量性得以保留,p的常量性会在复制形参过程中被忽略。

cpp 复制代码
int main() 
{
    const char* const p = "fun with pointer";
    test_value_pattern(p);
 }

四、总结

这一部分的类型推导法则主要是和C++11之前版本相关的,类型推导过程中按照普通函数的传参规则对const属性进行了取舍、这些行为主要依赖函数参数的表达方式。模板参数的类型按照"最小原则"推导,也就是说T放到函数参数中能清楚表达形参类型就可以了。后续部分会继续探索C++11万能引用相关的类型推导。

相关推荐
爱看书的小沐21 小时前
【小沐学GIS】基于C++瓦片地图下载工具(高德/天地图/谷歌/必应/OSM/MapBox/ArcGIS)第十三期
c++·webgl·谷歌地图·earth·osm·瓦片地图下载·mapdowloader
青草地溪水旁1 天前
EPOLLONESHOT事件类型和ET模式有什么区别?
服务器·网络·c++·epoll
charlie1145141911 天前
精读 C++20 设计模式:行为型设计模式 — 访问者模式
c++·学习·设计模式·访问者模式·c++20
linux kernel1 天前
第二十三讲:特殊类和类型转换
开发语言·c++
渡我白衣1 天前
深入剖析:boost::intrusive_ptr 与 std::shared_ptr 的性能边界和实现哲学
开发语言·c++·spring
怀旧,1 天前
【C++】26. 智能指针
开发语言·c++·算法
楼田莉子1 天前
vscode搭建C/C++配置开发环境
c语言·开发语言·c++·vscode·学习·编辑器
R-G-B1 天前
【14】C#实战篇——C++动态库dll 接口函数将char* strErr字符串 传给C# ,并且在winform的MessageBox和listbox中显示。C++ string 日志传给 C#
c++·c#·strerr字符串传给c#·动态库dll传递字符串给c#·string日志传给c#·c++ string传给 c#·c++底层函数日志传给c#显示
半桔1 天前
【STL源码剖析】从源码看 list:从迭代器到算法
java·数据结构·c++·算法·stl·list
拾光Ծ1 天前
【C++】STL之list模拟实现:关于链表容器的双向迭代器你知道多少?
开发语言·数据结构·c++·list·visual studio