1.受限名称和依赖型名称:如果一个名称使用: :或 . ->来显示作用域,则为受限名称。如果一个名称依赖于模板参数,我们称它为依赖型名称。
一:名称查找
受限名称和非受限名称的查找:
int x;
class B {
public:
int i;
};
class D : public B {
};
void f(D* pd)
{
pd->i = 3;//会找 b中的x
D::x = 2;//error 不会去外围作用域查找x
}
注:受限名称的名称查找在一个受限的作用域内部进行,如果该作用域是一个类,那么查找范围可以达到它的基类。但不会考虑外围作用域。
extern int count;//(1)
int lookup_example(int count)//(2)
{
if (count < 0) {
int count = 1;//(3)
lookup_example(count);//非受限 引用3
}
return count + ::count;//1.非受限 引用2 2.受限 引用1
}
注:非受限类型的普通查找方式。
add:非受限名称的另一种查找方式,依赖于参数查找(argument-dependent lookup, ADL)
参考:https://www.cnblogs.com/GnibChen/p/8599981.html
二:友元名称插入
考虑:
template <typename T>
class C
{
friend void f();
friend void f(C<T> const&);
};
void g(C<int>* p)
{
f();//f()在此可见吗
f(*p);//这个呢?
}
其中包含友元函数的类位于作用域A。
三:插入式类名称
在类本身作用域中插入该类的名称,我们称该名称为插入式类名称。可访问。
四,解析模板
一些例子:
1.x<1>(0) 如果词法分析发现x是模板名称则 0 被强转为 x(1)类型 否则分析为 (x < 1) > 0
2.
template<bool B>
class Invert {
public:
static bool const result = !B;
};
void g()
{
bool test = Invert<(1 > 0)>::result;//圆括号不能省略
}
3.之前说过的 List<List<int> > a;
c++ maximum munch扫描原则,让一个标记具有尽可能多的字符,所以>>会被看作右移
还有
class X{
};
List<::X> many_X;//error
因为 <:会被看作符号【 则为List [:X> many_X;
4.依赖型类型名称
例如:
template <typename T>
class Trap {
public:
enum { x };
};
template <typename T>
class Victim {
public:
int y;
int answer;
void poof()
{
//answer = Trap<T>::x*y;
}
};
template <>
class Trap<void>
{
public:
typedef int x;
};
void boom(Victim<void>& bomb)
{
bomb.poof();
}
注: 没有Trap<void> 特化之前 Trap<T>::X被理解为一个值, 特化之后为一个类型。
typename:当类型名称具有以下性质时,应该在名称前添加typename
1)名称出现在一个模板中
2)名称是受限的
3)名称不是用于指定基类继承的列表中,也不是位于引入构造函数的成员初始化列表中
4)名称依赖于模板参数
前面三个条件同时满足 用typename前缀。
(待理解)
5.依赖型模板名称
在限定运算符后面使用关键词template .
如果限定符号前面的内容的类型要依赖于模板参数并且紧跟限定符号后面的是template-id,那么就应该使用关键词typename
例如 p.template Deep<N>::f() 其中p的类型要依赖于模板参数T.
template <typename T>
class Shell {
public:
template <int N>
class In {
public:
template<int M>
class Deep {
public:
virtual void f();
};
};
};
template <typename T, int N>
class Weird
{
public:
void case1(typename Shell<T>::template In<N>::template Deep<N>* p) {
p->template Deep<N>::f();
}
void case2(typename Shell<T>::template In<N>::template Deep<N>& p) {
p.template Deep<N>::f();
}
};
6.using-declaration中的依赖型名称 todo?
(p.s. 书上这部分的特性限制有一些貌似被修正了。todo)
template <typename T>
class BXT {
public:
typedef T Mystery;
template<typename U>
struct Magic;
};
template <typename T>
class DXTT :private BXT<T>
{
public:
using typename BXT<T>::Mystery;
//using BXT<T>::Mystery; 貌似现在这种写法可以通过编译
Mystery* p;
};
(利用using引入名称类型 需要用typename显示指定,不过我本地不加typename编译也能通过)todo
template <typename T>
class DXTM :private BXT<T> {
public:
using BXT<T>::template Magic;
Magic<T>* plink;
};
(同样的书上说这种写法是非法的,但貌似现在标准规范修改了这个特性。)
7.ADL和显式模板实参
namespace N {
class X
{
};
template<int I> void select(X*);
}
void g(N::X* xp)
{
//using namespace N;
select<3>(xp);
}
注:这种情况下不能使用ADL找到N空间下的select模板 ,因为分析的时候不知道select是一个模板所以就不知道<3>是一个模板实参列表。
八:派生和类模板
1.非依赖型基类
指 无需知道模板实参就可以完全确定类型的基类。
template <typename X>
class Base
{
public:
int basefield;
typedef int T;
};
class D1 : public Base<Base<void> > {
public :
void f() { basefield = 3; }
};
template <typename T>
class D2 :public Base<double>
{
public:
void f() { basefield = 7; }//正常访问继承成员
T strange;//t是Base<double>::T 而不是模板参数T
};
void g(D2<int*>& d2, int* p)
{
d2.strange = p;//error 类型不匹配
}
注:对于模板中的非依赖型基类而言,如果在它的派生类中查找一个非受限名称,那么会先查找这个非依赖性基类,然后才查找模板参数列表。
2.依赖型基类
c++标准规定: 对于模板中的非依赖型名称,将在看到的第一时间进行查找。
template<typename T>
class DD :public Base<T> {
public:
void f() { basefield = 0; }
};
template<>
class Base<bool> {
public:
enum {basefield = 42};
};
void g(DD<bool>& d)
{
d.f();
}
由于以上的规则,我们在第一次看到非依赖名称basefield时进行了 查找,确定为int型 之后的显示特化改变了basefield的类型 所以调用g时会产生错误。
//todo