C++(八)——typename用法

1、typename来源

1.1原因1:

从表面上看,下面模板的参数只支持用户自定义类型,但其实对语言内置类型或者指针调用也支持:

template <class T>
int compare(const T &v1, const T &v2)
{
    if (v1 < v2) return -1;
    if (v2 < v1) return 1;
    return 0;
}
//用指针调用:
int v1=1 ,v2=2;
int ret= cmpare(v1, v2)

class在类和模板中的表现的意义存在一些不一致。前者针对用户自定义类型,后者包含了语言内置类型和制止呢。

1.2真实原因:

1.2.1一些关键概念:

限定名:限定了命名空间的名称。

std::cout; //std就是一个命名空间R

依赖名和非依赖名
依赖名是指依赖于模板参数的名称,而非依赖名则相反,指不依赖于模板参数的名称。

template<class T>
class Myclass
{
	int i;
	vector<int> vi;
	vector<int>::iterator vitr;  //非依赖名
	T t;
	vector<T> vt;
	vector<T>::iterator viter;  //依赖名
	//依赖于模板参数的名称就是依赖名
}

类作用域
在类外部访问类中的名字时,可以使用类作用域符,形如class::name的调用通常有三种:静态数据成员、静态成员函数和嵌套类型。

struct Myclass
{
static int A;  //静态数据成员
static int B(); //静态函数成员
typedef int C; //嵌套类型
}
1.2.2真实原因
template<class T>
void foo()
{T::iterator * iter;
}

T::iterator 这样的一个定义可以是以上三种类作用域中的任意一种类型。
(1)如果iterator是嵌套类型将正确执行,这段代码的意思是定义一个 T::iterator类型的数据。
(2)当iterator是静态数据成员时,以上代码将被解释为两个数相乘,返回值抛弃。如果iter没有定义,将报错;但如果iter是全局变量,将执行。
在模板实例化之前完全没有办法区分,因此,有必要引入新的关键字typename来区分这两种情况。

2、typename用法

2.1 C++标准

对于用于模板定义的依赖模板参数的名称,只有在实例化的参数中存在这个类型名,或者这个名称前面使用了typename关键字修饰,编译器才会将这个名字当做是类型。除了以上两种情况,编译器不会将它视为类型。
即,当你想告知编译器iterator是类型而不是变量,只需要用typename:

template<class T>
void foo()
{typename T::iterator * iter;
}

这时,编译器可以确定T::iterator是一个类型,而不需要等到实例化时再确定。

2.2使用typename的规则

typename在下面情况禁用:
模板定义之外,即typename只能用于模板中。
非限定类型,比如前面介绍过的int,vector之类。
基类列表中,比如template class C1: T::TnnerType
构造函数的初始化列表中
如果类型是依赖于模板参数的限定名,那么在它之前必须加typename(除非在类的初始化成员表中)
在其他情况下,typename是可选的,也就是说对于一个不是依赖名的限定名,该名称可选,比如vector vi;

2.3其他情况

对于不引起歧义的情况,仍然需要加typename,比如:

template<class T>
void foo(){
typename T::iterator_type iterator_type;
typedef typename _type_traits_<T>::iterator iterator_type;}//将_type_traits_<T>这个模板类中的iterator重命名为 iterator_type

2.4其他说明

对于制定模板参数这一用法,class和typename都支持,但typename更能准确表达含义。

### C++ 中 `->` 运算符的使用方法 #### 基本概念 在 C++ 中,`->` 是一种间接访问运算符,主要用于通过指针来访问对象的成员。它通常用于简化指针操作中的成员访问语法[^3]。 #### 用法解释 当有一个指向某个对象的指针时,可以通过 `->` 来直接访问该对象的公有成员,而无需显式解引用指针后再使用点号 (`.`) 访问成员。其作用相当于先对指针进行解引用(即取到实际的对象),然后再对该对象执行成员访问操作。 例如,在以下代码片段中展示了如何利用 `->` 对象指针访问结构体成员: ```cpp struct MyStruct { int x; void display() { std::cout << "Value: " << x << std::endl; } }; MyStruct myObj; MyStruct* pMyObj = &myObj; pMyObj->x = 20; // 使用 -> 通过指针设置成员变量 pMyObj->display(); // 调用指针所指向对象的方法 ``` 上述例子表明了两种情况下的应用:一是修改数据成员;二是调用成员函数。 #### 自定义类中重载 `operator->` 除了基本功能外,还可以自定义行为以支持更复杂的场景——比如智能指针的设计模式下就需要重新定义这个操作符的行为逻辑。这允许开发者创建类似于标准库提供的自动管理资源生命周期的功能强大的工具类型[^4]。 这里给出一个简单版本的模板化智能指针实现作为示范: ```cpp template <typename T> class SmartPointer { public: explicit SmartPointer(T* ptr): m_ptr(ptr) {} T* operator->() const { return m_ptr; } private: T* m_ptr; }; ``` 在这个案例里,我们看到每当用户尝试经由SmartPointer实例去请求目标实体上的任何东西的时候,实际上都会触发我们的定制版arrow-operator处理流程,并最终返回原始裸指针对应的结果给调用方继续完成后续动作链路。 另外值得注意的一点是关于箭头运算符的一个有趣的小技巧可以用来计算特定字段相对于整个结构体起始位置字节偏移量[^5]: ```cpp #include <iostream> struct Vector3 { float x, y, z; }; int main(){ int offset_y = (char*)&(((Vector3*)nullptr)->y); std::cout<<offset_y<<std::endl; } ``` 此程序打印出了`y`成员距离结构体首部有多少个字节的距离。 ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值