> 文章列表 > C++模板基础(六)

C++模板基础(六)

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();
}

C++模板基础(六)

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; //OKy.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(); //OKreturn 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();
}

C++模板基础(六)

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 parametervoid 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; //OKT tmp2; //OK}
};int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);B<int> x;x.fun<float>(); //OKreturn 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; //OKT 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; //OKB<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;
}

C++模板基础(六)

#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;
}

C++模板基础(六)

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;
}

C++模板基础(六)

– 特化版本与基础版本可以拥有完全不同的实现

#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++模板基础(六)

● 类模板的实参推导(从 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); //OKreturn 0;
}

参考
深蓝学院:C++基础与深度解析
cppinsights
cppreference