C++模板——待决名
C++模板——待决名
概念
参考官方文档:
简单来说:
template<typename T>
struct X {
void f_x(){}
};
template<typename T>
struct Y : X<T> {
void f_y() {
f_x(); // 非待决名,函数查找是在不知道T的具体类型时(即模板未实例化)就查找【立即绑定】
this->f_x(); // 待决名,函数查找是在知道T的具体类型时(即模板已实例化)才查找【待会绑定】
}
}
我们应该在待决名的情况下,使用待决名的名字;在非待决名的情况下,使用非待决名的名字。
在模板中的使用
参考官方文档:无限定的名字查找 - cppreference.com
问题
问题1:
#include <iostream>
template<typename T = int>
class Base {
public:
Base() :m_a(0) {}
Base(T a) :m_a(a) {}
virtual void func(){
// 假设T类型重载了<<运算符且能被输出
std::cout << "Base func()" << m_a << std::endl;
}
protected:
T m_a;
};
template<typename T = int>
class Derived :public Base<T> {
public:
Derived() : Base<T>() {}
Derived(T a, T b) :Base<T>(a), m_b(b) {}
void func() override {
// 假设T类型重载了<<运算符且能被输出
std::cout << "Derived func()" << m_a << " " << m_b << std::endl; // m_a是非待决名
}
private:
T m_b;
};
int main() {
Derived o(2.3, 1.4);
o.func();
return 0;
}
上述代码中,这句std::cout << "Derived func()" << m_a << " " << m_b << std::endl;报错:m_a未声明的标识符。
解决方案:
- 法1,让成员成为待决名的:将使用
m_a改为this->m_a,或使用显式指定m_a命名空间为Base<T>::m_a - 法2,保留成员为非待决名不动,让模板类实例化:在继承模板类时将模板类实例化为特定的类型,比如
Base<int>,就不会报错了
问题2:
同问题1,本例摘自《C++20实践入门(第6版)》
template<typename T>
class Base {
public:
void baseFun();
protected:
int m_base_var;
};
template<typename T>
class Derived : public Base<T> {
public:
void deviredFun();
};
template<typename T>
void Derived<T>::deviredFun() {
baseFun(); //报错:baseFun()未定义
std::cout << m_base_var; //报错:m_base_var未定义
}
静态检查报错的原因是,子类Derived虽然继承自Base,但是Base可能存在一个没有定义baseFun()或m_base_var的特化类。这样继承就出现问题了。(即编译器将baseFun()和m_base_var解释为非从属名,但是我们希望它们是从属名)
解决方案是
-
法1:在
baseFun()和m_base_var前添加this->,强制地让编译器假定这是Base<T>中的成员,从而推迟检查到模板实例化时(待决名)。 -
法2:在
baseFun()和m_base_var前添加Base<T>::,和法1原理相同 -
法3:由于法1和法2都是需要在所有用到的地方添加,很麻烦,因此可以用
using指定:template<typename T> class Derived : public Base<T> { public: using Base<T>::baseFun(); void deviredFun(); protected: using Base<T>::m_base_var; //基类的成员这里得using声明一下了,不是定义 };
问题3:
// 邻接矩阵
template <typename T = char>
struct MGraph {
T vex[MAX_VERTEX_NUM]; // 顶点表(用来存放顶点的信息)
int edge[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; // 邻接矩阵(用来存放边和边权)
int vex_num, arc_num; // 当前的顶点数和弧数
};
// 邻接表
struct ArcNode { // 边表结点
int adjvex; // 出边终点在顶点表中的下标
ArcNode* next;
};
template <typename T = char>
struct VNode { // 顶点表结点
T data;
ArcNode* first;
};
template <typename T = char>
struct ALGraph { // 邻接表类型
VNode<T> node[MAX_VERTEX_NUM]; // 顶点表,其中的每个顶点都连着一个出边表
int vex_num, arc_num; // 当前的顶点数和弧数
};
// 将邻接表转为邻接矩阵
template <typename T = char>
void convertToMatrix(ALGraph<T>& g, MGraph<T>& m) {
for (int i = 0; i < g.vex_num; i++) {
auto p = g.node[i].first; // first是非待决名。不过这里编译能通过,只不过这里写auto的话用不了代码提示了
while (p != nullptr) {
m.edge[i][p->adjvex] = 1;
p = p->next;
}
}
}
解决方案:
- 法1,让
first成为待决名的,即用typename关键字显式指定p的类型:typename Graph::ArcNode* p = g.node[i].first; - 法2,保留成员为非待决名不动,让模板函数实例化

浙公网安备 33010602011771号