C模板是什么?能否详细介绍一下?

2026-04-16 23:331阅读0评论SEO资源
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1385个文字,预计阅读时间需要6分钟。

C模板是什么?能否详细介绍一下?

1. 函数模板定义:`template void f1(T);` - 参数说明:`f1` 函数接收一个类型为 `T` 的参数。 - 使用示例:`f1(i);`,其中 `i` 是左值。 - 错误示例:`f1(ci);`,其中 `ci` 是右值。 - 类型约束:`T` 类型是 `const int`,例如 `f1(5);`

2. 常量参数函数模板:`template void f2(const T);` - 参数说明:`f2` 函数接收一个常量类型为 `T` 的参数。 - 使用示例:`f2(i);`,其中 `i` 可以是左值或右值。 - 错误示例:`f2(ci);`,其中 `ci` 是右值,不适用。

1.参数类型

template <typename T> void f1(T&);//实参必须是左值 f1(i);//对 f1(ci);//对,T的类型是const int f1(5);//错 template <typename T> void f2(const T&);//实参可以是左值,const右值 f2(i);//对 f2(ci);//对 f2(5);//对 template <typename T> void f3(T&&);//实参只能是非const右值

2.右值引用参数的模版函数

template <typename T> void f3(T&& val) { T t=val;//拷贝还是绑定一个引用? t=fcn(t);//赋值只改变t还是t和val都变? if (val == t)//若t是引用类型,则一直为true …… } //看传入的值 1.如果传入值是右值,如字面常量,T为int。此时t的类型也为int,通过val初始化,参数val保持不变 2.如果传入值是左值,T为int&???此时t的类型也为int&,则变t也变val

3.标准库的move

template <typename T> typename remove_reference<T>::type&& move(T&& t) { return static_cast<typename remove_reference<T>::type&&>(t); }

std::move (string("bye!"))执行过程:

  • 推断T的类型为string
  • remove_reference用string实例化
  • remove_reference<string>的type成员是string
  • move的返回类型是string&&
  • move的函数参数t的类型为string&&

4.转发的类型保持

template <typename F, typename T1, typename T2> void flip1(F f,T1 t1,T2 t2) { f(t2,t1); } void f(int v1, int &v2) { } // f(42,i) flip1(f,j,42);//没有发挥引用的效果

解决办法:模版类型参数是右值引用,对应实参的const属性和左右值属性得到保持。为什么?引用折叠。

template <typename F, typename T1, typename T2> void flip1(F f,T1&& t1,T2&& t2) { f(t2,t1); }

还有个问题:

flip(g,i,42);//不能从一个左值实例化int &&

C模板是什么?能否详细介绍一下?

解决办法:

template <typename F, typename T1, typename T2> void flip1(F f,T1&& t1,T2&& t2) { f(std::forward<T2>(t2),std::forward<T1>(t1));//显式模版实参类型 }

4.模版重载

//1. template <typename T> string debug_rep(const T& t) { stream ret; ret<<t; return ret.str(); } //2. template <typename T> string debug_rep(T* p) { stream ret; ret<<"pointer is: "<<p; if (p) ret<<" "<<debug_rep(*p); else ret<<" null ptr"<<; return ret.str(); } //调用 string s("hi"); cout<<debug_rep(s)<<endl;//只有第一个版本可行 cout<<debug_rep(&s)<<endl;//第一个版本实例化为:debug_rep(const string*&), T类型为string* //第二个版本实例化为:debug_rep(string*),T类型为string  更精确,编译器选择第二个 const string *sp=&s; cout<<debug_rep(sp)<<endl;//第一个版本实例化为:debug_rep(const string*&), T类型为string* //第二个版本实例化为:debug_rep(const string*),T类型为const string,更特列化,选择第二个

一个非函数模版和一个函数模版都能提供同样的匹配时,编译器选择非函数模版。

多个函数模版提供同样的匹配时,编译器选择最特例化的那个。

5.可变函数模版

template <typename T, typename ... Args> viod foo(const T& t, const Args& ... rest)
{
  cout<<sizeof...(Args)<<endl;//类型参数的数目
  cout<<sizeof...(rest)<<endl;//函数参数的数目
} // int i=0; double d=3.14; string s="stringiest "; foo(i,s,42,d);  //参数包有3个参数 foo(s,42,"hi"); //参数包有2个参数 foo(d,s);    //参数包有1个参数 foo("hi");   //参数包有0个参数 //实例化版本 void foo(const int&, const string&, const int&, const double&); void foo(const string&, const int&, const char[3]&); void foo(const double&, const string&); void foo(const char[3]&);

递归调用:必须声明一个非可变参数版本,否则无限递归

template<typename T> ostream &print(ostream &os, const T& t)//打印最后一个元素 { return os<<t; } template <typename T, typename... Args> ostream &print(ostream& os, const T& t, const Args&... rest) { os<<t<<","; return print(os, rest ...);//递归调用 }

5.1 扩展

5.2 转发(forward)

6.模版特例化:本质上实例化一个模板,而不是重载,不影响函数匹配

template <typename T> int compare(const T&, const T&); template <size_t N, size_t M> int compare(const char (&)[N],const char (&)[M]); const char* p1="hi",*p2="mom"; compare(p1,p2);//调用第一个版本 compare("hi","mom");//调用两个版本 template <> int compare(const char* const &p1, const char* const &p2) { return strcmp(p1,p2); } //此特例化,是为了处理字符指针,而不是数组

模版及其特例化版本应声明在同一个头文件中,同名模版的声明应放在前面,然后是这些模版的特例化版本。

6.1 类模板特列化

6.2 类模板部分特列化

本文共计1385个文字,预计阅读时间需要6分钟。

C模板是什么?能否详细介绍一下?

1. 函数模板定义:`template void f1(T);` - 参数说明:`f1` 函数接收一个类型为 `T` 的参数。 - 使用示例:`f1(i);`,其中 `i` 是左值。 - 错误示例:`f1(ci);`,其中 `ci` 是右值。 - 类型约束:`T` 类型是 `const int`,例如 `f1(5);`

2. 常量参数函数模板:`template void f2(const T);` - 参数说明:`f2` 函数接收一个常量类型为 `T` 的参数。 - 使用示例:`f2(i);`,其中 `i` 可以是左值或右值。 - 错误示例:`f2(ci);`,其中 `ci` 是右值,不适用。

1.参数类型

template <typename T> void f1(T&);//实参必须是左值 f1(i);//对 f1(ci);//对,T的类型是const int f1(5);//错 template <typename T> void f2(const T&);//实参可以是左值,const右值 f2(i);//对 f2(ci);//对 f2(5);//对 template <typename T> void f3(T&&);//实参只能是非const右值

2.右值引用参数的模版函数

template <typename T> void f3(T&& val) { T t=val;//拷贝还是绑定一个引用? t=fcn(t);//赋值只改变t还是t和val都变? if (val == t)//若t是引用类型,则一直为true …… } //看传入的值 1.如果传入值是右值,如字面常量,T为int。此时t的类型也为int,通过val初始化,参数val保持不变 2.如果传入值是左值,T为int&???此时t的类型也为int&,则变t也变val

3.标准库的move

template <typename T> typename remove_reference<T>::type&& move(T&& t) { return static_cast<typename remove_reference<T>::type&&>(t); }

std::move (string("bye!"))执行过程:

  • 推断T的类型为string
  • remove_reference用string实例化
  • remove_reference<string>的type成员是string
  • move的返回类型是string&&
  • move的函数参数t的类型为string&&

4.转发的类型保持

template <typename F, typename T1, typename T2> void flip1(F f,T1 t1,T2 t2) { f(t2,t1); } void f(int v1, int &v2) { } // f(42,i) flip1(f,j,42);//没有发挥引用的效果

解决办法:模版类型参数是右值引用,对应实参的const属性和左右值属性得到保持。为什么?引用折叠。

template <typename F, typename T1, typename T2> void flip1(F f,T1&& t1,T2&& t2) { f(t2,t1); }

还有个问题:

flip(g,i,42);//不能从一个左值实例化int &&

C模板是什么?能否详细介绍一下?

解决办法:

template <typename F, typename T1, typename T2> void flip1(F f,T1&& t1,T2&& t2) { f(std::forward<T2>(t2),std::forward<T1>(t1));//显式模版实参类型 }

4.模版重载

//1. template <typename T> string debug_rep(const T& t) { stream ret; ret<<t; return ret.str(); } //2. template <typename T> string debug_rep(T* p) { stream ret; ret<<"pointer is: "<<p; if (p) ret<<" "<<debug_rep(*p); else ret<<" null ptr"<<; return ret.str(); } //调用 string s("hi"); cout<<debug_rep(s)<<endl;//只有第一个版本可行 cout<<debug_rep(&s)<<endl;//第一个版本实例化为:debug_rep(const string*&), T类型为string* //第二个版本实例化为:debug_rep(string*),T类型为string  更精确,编译器选择第二个 const string *sp=&s; cout<<debug_rep(sp)<<endl;//第一个版本实例化为:debug_rep(const string*&), T类型为string* //第二个版本实例化为:debug_rep(const string*),T类型为const string,更特列化,选择第二个

一个非函数模版和一个函数模版都能提供同样的匹配时,编译器选择非函数模版。

多个函数模版提供同样的匹配时,编译器选择最特例化的那个。

5.可变函数模版

template <typename T, typename ... Args> viod foo(const T& t, const Args& ... rest)
{
  cout<<sizeof...(Args)<<endl;//类型参数的数目
  cout<<sizeof...(rest)<<endl;//函数参数的数目
} // int i=0; double d=3.14; string s="stringiest "; foo(i,s,42,d);  //参数包有3个参数 foo(s,42,"hi"); //参数包有2个参数 foo(d,s);    //参数包有1个参数 foo("hi");   //参数包有0个参数 //实例化版本 void foo(const int&, const string&, const int&, const double&); void foo(const string&, const int&, const char[3]&); void foo(const double&, const string&); void foo(const char[3]&);

递归调用:必须声明一个非可变参数版本,否则无限递归

template<typename T> ostream &print(ostream &os, const T& t)//打印最后一个元素 { return os<<t; } template <typename T, typename... Args> ostream &print(ostream& os, const T& t, const Args&... rest) { os<<t<<","; return print(os, rest ...);//递归调用 }

5.1 扩展

5.2 转发(forward)

6.模版特例化:本质上实例化一个模板,而不是重载,不影响函数匹配

template <typename T> int compare(const T&, const T&); template <size_t N, size_t M> int compare(const char (&)[N],const char (&)[M]); const char* p1="hi",*p2="mom"; compare(p1,p2);//调用第一个版本 compare("hi","mom");//调用两个版本 template <> int compare(const char* const &p1, const char* const &p2) { return strcmp(p1,p2); } //此特例化,是为了处理字符指针,而不是数组

模版及其特例化版本应声明在同一个头文件中,同名模版的声明应放在前面,然后是这些模版的特例化版本。

6.1 类模板特列化

6.2 类模板部分特列化