模版类型推导

简述

  • 采用如下方式讨论模版类型推导原则
//声明
template<typename T>
void f(ParamType param);
//调用
f(expr);
  • 根据ParamType的类型分为三类来阐述

    1. ParamType是指针或者引用(非universal reference,它与lvalue reference和rvalue reference不同)
    2. ParamType为universal reference
    3. ParamType非指针和引用

Case1

这是最简单的一种情况,它的工作过程如下:
1. 如果expr是一个引用类型,忽略引用部分
2. 之后expr与ParamType进行模式匹配来决定T

模版如下

template<typename T>
void f(T& param);

声明以下变量,则推导如注释所示

int x = 27;
const int cx = x;
const int& rx = x;

f(x); // T->int, ParamType->int
f(cx); // T->const int, ParamType->const int&
f(rx); // T->const int, ParamType->const int&

可见后两者的const都被推导到了T类型中,这也就导致了ParamType具有了const属性,从而维护了调用者的原意:保证引用参数的常量性。


该用模版如下,为param添加const属性

template<typename T>
void f(const T& param);

声明以下变量,则推导如注释所示,并没有什么特别大的变化,只是T不再附加const,因为param已经具备的const

int x = 27;
const int cx = x;
const int& rx = x;

f(x); // T->int, ParamType->int
f(cx); // T->int, ParamType->const int&
f(rx); // T->int, ParamType->const int&

对于ParamType为指针类型模版,如下

template<typename T>
void f(T* param);

template<typename T>
void f(const T* param);

推导规则与引用的是一样一样的。

Case2

universal reference的声明类型类似右值引用 T&&,推导规则如下:
1. 如果expr为lvalue,T和ParamType全部推导为lvalue reference。这存在两个不同点:1. 这是在模版类型推导中唯一的一种情况:T推导为引用类型。2.虽然ParamType采用T&& 右值引用的声明语法,但是实际推导出的类型为lvalue reference。
2. 如果expr为lvalue,则采用Case1的推导方式。

template <typyname T>
void f(T&& param); //universal reference T&&

int x = 27;
const int cx = 27;
const int& rx = x;

f(x); //lvalue, T->int&, ParamType->int&
f(cx); //lvalue, T->const int&, ParamType->const int&
f(rx); //lvalue, T->const int&, ParamType->const int&
f(27); //rvalue, T->int, ParamType->int&&
//后期解释为什么,关键在于unisersal reference对于lvalue和rvalue的推导是不同的

Case3

情形如下

template<typename T>
void f(T param);

可见参数按值传递,每次param都是一个拷贝,一个新的对象。
这也就决定了我们如何确定推导规则:
1. 如果expr是一个引用类型,忽略引用部分。
2. 如果忽略了引用之后,expr含有const,同样忽略;如果有volatile,忽略

int x = 27;
const int cx = x;
const int& rx = x;
f(x) //T->int, ParamType->int
f(cx) //T->int, ParamType->int
f(rx) //T->int, ParamType->int

小节

个人理解:对于模版类型推导,也就是根据用户所传递参数的类型expr以及提供的函数模版形式,综合二者来推测函数调用者的原意,再找到能够体现这种原意的最合适的推导结果,也就是T和ParamType。
Case1中引用或者指针都代表(指向)着一个已存在的对象,对象的const、volatile属性是需要保留的,所以最终形成的ParamType都将具备这些属性。
Case2还没有太理解。
Case3之所以抛弃掉了const、volatile属性,是因为值传递的话是一个拷贝过程,与用户传递的参数属性并不意味着一定要做用到那个拷贝身上。
对于const char* const 类型的expr,推导如下,同样要保持用户的原意

const char* const ptr = "hello world";
f(ptr); //T->const char*, ParamType->const char*

Arrays and Function Argument

Arrays

  • 数组类型和指针类型
    数组类型和指针类型是两种不同的类型,在很多情况下一个array可以蜕化成为一个指向array第一个元素的指针。所以以下代码可以通过编译
const char name[] = "hello world"; //name类型是const char[12]
const char *ptrToName = name; //蜕化为const char*
  • array在传值模版中的推导
template<typename T>
void f(T param);

f(name); // T->const char*
/*这是因为在C语言中
void f(int param[]);
等价于
void f(int *param);
*/
  • array在传引用模版中的推导
template<typename T>
void f(T& param);

f(name); //T->const char[12], ParamType->const char(&)[12]

function

数组不是唯一的蜕化为指针的东西,函数同样也蜕化为指针。

void someFunc(int ,double);
template<typename T>
void f1(T param); //by value
template<typename T>
void f2(T &param); //by ref

f1(someFunc); //ParamType->void(*)(int ,double)
f2(someFunc); //ParamType->void(&)(int ,double)
### C++ 构造函数模板的类型推导机制 在 C++ 中,构造函数模板允许类通过模板参数实现通用性和灵活性。当调用带有模板参数的构造函数时,编译器会尝试根据传入的实际参数自动推导模板参数的具体类型。以下是关于构造函数模板类型推导的一些重要规则和细节: #### 1. 基本原理 构造函数模板类似于普通函数模板,在实例化过程中,编译器会基于实际传递的参数类型推导模板参数 `T` 的具体类型[^1]。例如: ```cpp template<typename T> class MyClass { public: MyClass(T value) {} }; int main() { MyClass obj(10); // 编译器推导出 T 为 int } ``` 在此例中,由于传递的是整数值 `10`,因此模板参数 `T` 被成功推导为 `int`。 --- #### 2. 参数类型的匹配与转换 如果传递给构造函数的参数可以隐式转换为目标类型,则这种情况下仍然能够完成类型推导[^3]。例如: ```cpp #include <string> template<typename T> struct Sum { T value; template<typename... Args> explicit Sum(Args&&... args) : value{(args + ...)} {} }; Sum s("hello", std::string(" world")); // 推导出 T 为 std::string ``` 这里,第一个参数 `"hello"` 是一个 `const char*` 类型,而第二个参数是一个 `std::string`。通过使用 `std::common_type_t`,最终推导出 `T` 为 `std::string`。 需要注意的是,如果无法找到合适的公共类型或者存在歧义,则会导致编译错误。 --- #### 3. 显式指定模板参数 尽管大多数时候可以通过上下文环境让编译器自行推导模板参数,但在某些复杂场景下可能需要显式指明具体的类型。例如: ```cpp MyClass<int> obj('a'); // 明确告诉编译器 T 应该是 int ``` 此时即使 `'a'` 实际上是一个字符常量(即 `char`),但由于我们强制设定了 `T=int`,所以它会被提升为对应的整数表示形式后再参与初始化过程。 --- #### 4. 用户自定义类型的支持 对于更复杂的用户定义数据结构来说,只要满足基本条件——比如拥有可接受相应输入值的操作符重载方法等——同样适用于上述原则。考虑如下情况: ```cpp template<class Key, class Compare=std::less<Key>, class Alloc=std::allocator<Key>> class Set {}; Set<std::pair<const int,std::string>> my_set {{1,"one"},{2,"two"}}; // 这里明确给出了键值对作为元素存储于集合之中。 ``` 此片段展示了如何借助已知类别创建新实体的同时保持高度定制能力[^4]。 --- #### 总结 综上所述,C++ 提供了一套强大而又灵活的机制用来处理各种不同情形下的对象构建需求;无论是简单基础的数据还是复合抽象的概念都能很好地融入这套体系当中去运作起来。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值