C++模板基础(六)
类模板与成员函数模板
● 使用 template 关键字引入模板: template class B {…};
– 类模板的声明与定义 翻译单元的一处定义原则
template<typename T>
class B; //类模板的声明
template<typename T>
class B //类模板的定义
{
};
template<typename T>
class B //类模板必须满足翻译单元级别的一处定义原则,Error: Redefinition of 'B'
{
};
– 成员函数只有在调用时才会被实例化
template<typename T>
class B //类模板的定义
{
public:
void fun(T input)
{
std::cout << input << std::endl;
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
B<int> x;
x.fun(3);
return a.exec();
}
template<typename T>
class B
{
public:
void fun(T input)
{
std::cout << input << std::endl; //#1: Error: main.cpp:9:19: Invalid operands to binary expression ('std::ostream' (aka 'basic_ostream<char, char_traits<char>>') and 'Str')
}
};
struct Str{};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
B<Str> y; //OK
y.fun(Str{}); //该语句引发了#1处的报错,证明了成员函数只有在调用时才会被实例化
return a.exec();
}
– 类内类模板名称的简写
template<typename T>
class B
{
public:
auto fun()
{
return B<T>{}; //OK
//如果该语句简写为return B{};,那么编译器自动视它为return B<int>{};
//如果模板参数为template<typename T1, typename T2> class B;,那么编译器自动视return B{};为return B<T1, T2>{};
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
B<int> x;
x.fun(); //OK
return a.exec();
}
– 类模板成员函数的定义(类内、类外)
template<typename T>
class B
{
public:
void fun(); //类模板成员函数的声明
};
template<typename T>
void B<T>::fun() //OK, 规范类模板成员函数的类模板外定义
{
}
void B<T>::fun() //Oops, 错误的类模板成员函数的类模板外定义
{
}
● 成员函数模板
– 类的成员函数模板
class B
{
public:
template <typename T>
void fun() //类的成员函数模板的类内定义
{
std::cout << "template <typename T> void fun()\n";
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
B x;
x.fun<int>();
return a.exec();
}
class B
{
public:
template <typename T>
void fun(); //类的成员函数模板的类内声明
};
template<typename T>
void B::fun() //类的成员函数模板的类外定义
{
std::cout << "template <typename T> void fun()\n";
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
B x;
x.fun<int>();
return a.exec();
}
– 类模板的成员函数模板
template<typename T> //#1
class B
{
public:
template <typename T> //#2, 隐藏了#1处的T, Warning: Declaration of 'T' shadows template parameter
void fun()
{
T //是#1处的T还是#2处的T?
}
private:
T m_data; //#1处的T, OK
};
template<typename T>
class B
{
public:
template <typename T2>
void fun() //类模板的成员函数模板的类内定义
{
T2 tmp1; //OK
T tmp2; //OK
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
B<int> x;
x.fun<float>(); //OK
return a.exec();
}
template<typename T>
class B
{
public:
template <typename T2>
void fun(); //类模板的成员函数模板的类内声明
};
template<typename T>
template <typename T2>
void B<T>::fun() //类模板的成员函数模板的类外定义
{
T2 tmp1; //OK
T tmp2; //OK
}
用google搜索引擎搜索gcc github vector可以搜到vector的实现
● 友元函数(模板)
template <typename T2>
void fun(); //友元函数模板的声明
template<typename T>
class B
{
template <typename T2>
friend void fun(); //声明该函数模板为类的模板的友元
int x;
};
template <typename T2>
void fun() //定义
{
B<int> tmp1;
tmp1.x; //OK
B<char> tmp2;
tmp2.x; //OK
}
– 可以声明一个函数模板为某个类(模板)的友元
template<typename T>
class B
{
friend void fun(B input) //被编译器解析为B<T>
{
std::cout << input.x << std::endl;
}
int x = 3;
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
B<int> val;
fun(val);
B<float> val2;
fun(val2);
return a.exec();
}
template<typename T>
class B
{
friend auto operator + (B input1, B input2) //由#1,被视为B<int>,是B<int>的友元函数,但不是B<float>的友元函数
{
B res;
res.x = input1.x + input2.x;
B<float> tmp; //
tmp.x; //Error: 'x' is a private member of 'B<float>'
return res;
}
int x = 3;
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
B<int> val1;
B<int> val2;
B<int> res = val1 + val2; //#1,此处用int实例化
return a.exec();
}
– C++11 支持声明模板参数为友元
template<typename T>
class B
{
friend T; //OK Since C++11
};
● 类模板的实例化 (class_template)
– 与函数实例化很像
– 可以实例化整个类,或者类中的某个成员函数
● 类模板的(完全)特化 / 部分特化(偏特化)
#include<iostream>
template <typename T>
struct B
{
void fun()
{
std::cout << "template <template T> void B::fun()" << std::endl;
}
};
template<>
struct B<int>
{
void fun()
{
std::cout << "template<> void B<int> fun()" << std::endl;
}
};
int main()
{
B<int> x;
x.fun();
return 0;
}
#include<iostream>
template <typename T, typename T2>
struct B
{
void fun()
{
std::cout << "template <template T, typename T2> void B::fun()" << std::endl;
}
};
template<typename T>
struct B<int, T>
{
void fun2()
{
std::cout << "template<typename T> void B<int, T> fun2()" << std::endl;
}
};
int main()
{
B<int, double> x;
x.fun2();
return 0;
}
template <typename T, typename T2>
struct B
{
void fun()
{
std::cout << "template <template T> void B::fun()" << std::endl;
}
};
template<typename T> //更换模板参数名称并不会对模板的代码逻辑产生影响
struct B<int, T>
{
void fun2()
{
std::cout << "template<> void B<int> fun2()" << std::endl;
}
};
#include<iostream>
template <typename T>
struct B
{
void fun()
{
std::cout << "template <template T> void B::fun()" << std::endl;
}
};
template<typename T>
struct B<T*> //部分特化
{
void fun2()
{
std::cout << "template<typename T> void B<T*> fun2()" << std::endl;
}
};
int main()
{
B<int*> x;
x.fun2();
return 0;
}
– 特化版本与基础版本可以拥有完全不同的实现
#include<iostream>
template <typename T>
struct B
{
void fun()
{
std::cout << "template <template T> void B::fun()" << std::endl;
}
};
template<>
struct B<int>
{
void fun2()
{
std::cout << "template<> void B<int> fun2()" << std::endl;
}
};
int main()
{
B<int> x;
//x.fun(); //Error: class B<int>没有成员fun()
x.fun2();
return 0;
}
● 类模板的实参推导(从 C++17 开始)
– 基于构造函数的实参推导
#include<iostream>
template <typename T>
struct B
{
B(T input)
{}
void fun()
{
std::cout << "template <template T> void B::fun()" << std::endl;
}
};
int main()
{
B x(3);
return 0;
}
//CPPINSIGHTS里面的输出
#include<iostream>
template<typename T>
struct B
{
inline B(T input)
{
}
inline void fun()
{
std::operator<<(std::cout, "template <template T> void B::fun()").operator<<(std::endl);
}
};
/* First instantiated from: insights.cpp:15 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct B<int>
{
inline B(int input)
{
}
inline void fun();
};
#endif
/* First instantiated from: insights.cpp:15 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
B(int input) -> B<int>;
#endif
int main()
{
B<int> x = B<int>(3);
return 0;
}
#include<iostream>
template <typename T>
struct B
{
B(T* input)
{}
void fun()
{
std::cout << "template <template T> void B::fun()" << std::endl;
}
};
int main()
{
int i = 3;
B x(&i);
return 0;
}
//CPPINSIGHTS里面的输出
#include<iostream>
template<typename T>
struct B
{
inline B(T * input)
{
}
inline void fun()
{
std::operator<<(std::cout, "template <template T> void B::fun()").operator<<(std::endl);
}
};
/* First instantiated from: insights.cpp:16 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct B<int>
{
inline B(int * input)
{
}
inline void fun();
};
#endif
/* First instantiated from: insights.cpp:16 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
B(int * input) -> B<int>;
#endif
int main()
{
int i = 3;
B<int> x = B<int>(&i);
return 0;
}
– 用户自定义的推导指引
– 注意:引入实参推导并不意味着降低了类型限制!
#include<iostream>
#include<utility>
int main()
{
std::pair<int, double> x{ 3, 3.14 };
x.first = "123456"; //Error: 不能将 "const char *" 类型的值分配到 "int" 类型的实体
return 0;
}
– C++ 17 之前的解决方案:引入辅助模板函数
#include<iostream>
#include<utility>
int main()
{
std::pair x{ 3, 3.14 }; //Error: 缺少类模板 "std::pair" 的参数列表
return 0;
}
#include<iostream>
#include<utility>
template<typename T1, typename T2>
std::pair<T1, T2> make_pair(T1 val1, T2 val2)
{
return std::pair<T1, T2>(val1, val2);
}
int main()
{
auto x = make_pair(3, 3.14); //OK
return 0;
}
参考
深蓝学院:C++基础与深度解析
cppinsights
cppreference