CPP-Templates-2nd--第一章

目录

1.1.3 两阶段编译检查(Two-Phase Translation ) 

1.2 模板参数推断 

1.3 多个模板参数

1.3.1 作为返回类型的模板参数

1.3.2 返回类型推断

1.3.3 将返回类型声明为公共类型(Common Type)

1.4 默认模板参数

1.5 函数模板的重载 


 

参考:Walton1128/CPP-Templates-2nd--: 《C++ Templates 第二版》中文翻译,和原书排版一致,第一部分(1至11章)以及第18,19,20,21、22、23、24、25章已完成,其余内容逐步更新中。 个人爱好,发现错误请指正 (github.com)

1.类型检查:验证操作接受的是否是合适的类型。

2.static_assert: 

参考:静态断言 static_assert_喜欢打篮球的普通人的博客-CSDN博客

通常情况下,断言就是将一个返回值总是需要为真的判断表达式放在语句中,用于排除在设计的逻辑上不应该产生的情况。assert 是一个运行时断言。

 静态断言 static_assert,所谓静态就是在编译时就能够进行检查的断言,使用时不需要引用头文件。静态断言的另一个好处是,可以自定义违反断言时的错误提示信息。

1.1.3 两阶段编译检查(Two-Phase Translation ) 

模板是分两阶段编译的:

1.模板定义阶段只进行语法检查(少了分号)、未使用模板参数的静态断言。

2.模板实例化阶段,检查那些依赖于模板类型参数的部分

1.2 模板参数推断 

在类型推断的时候自动的类型转换是受限制的:

1.调用参数是按引用传递,任何类型转换都是不允许的。通过模板参数类型T定义的两个参数,实参类型必须一样:

template 
T1 min(T1 &  a, T1 &  b) {
    return a < b ? b : a;
}
int main() {
    
    min(t1, c);//报错 E0304	没有与参数列表匹配的 函数模板 "min" 实例

    return 0;
}

2.按值传递:只有退化(decay)这一简单的转换是允许的:忽略 const volatile 引用。数组函数被转换成指针。通过模板类型参数 T 定义的两个参数,实参的退化(decay)类型必须一样。

template
T max (T a, T b);

max(4, 7.2); // ERROR: 不确定 T 该被推断为 int 还是 double
std::string s;
foo("hello", s); //ERROR: 不确定 T 该被推断为 char const[6] 还是 std::string

1.3 多个模板参数

最好的方法是第二个 : 使用auto。

1. 引入第三个模板参数作为返回类型。

2. 让编译器找出返回类型。 

3. 将返回类型定义为两个参数类型的“公共类型”

1.3.1 作为返回类型的模板参数

模板类型推断不会考虑返回类型:

template
RT max (T1 a, T2 b); …
::max(4, 7.2); // OK, 但是太繁琐

 RT 不会被推断,所以要显式声明。

一般来说我们必须显式指定所有模板参数的类型,直到某一个模板参数的类型可以被推断出来为止。使用单模板参数的版本。:

template
RT max (T1 a, T2 b); …
::max(4, 7.2) //OK: 返回类型是 double,T1 和 T2 根据调用参数推断

1.3.2 返回类型推断

使用auto:

请注意,在初始化 auto 变量的时候其类型总是退化之后了的类型。当返回类型是 auto 的时候也是这样。

int i = 42;
int const& ir = i; // ir 是 i 的引用
auto a = ir; // a 的类型是 it decay 之后的类型,也就是 i

1.3.3 将返回类型声明为公共类型(Common Type)

 类型萃取(type trait) 定义在中 std::common_type.

它返 回一个结构体,结构体的 type 成员被用作目标类型。因此其主要应用场景如下:

 typename std::common_type::type //since C++11

简化后的版本变成:

 std::common_type_t // equivalent since C++14

std::common_type<>也是会做类型退化的,因此返回类型不会是引用: 

#include 
template
std::common_type_t max (T1 a, T2 b)
{
return b < a ? a : b;
}

1.4 默认模板参数

利用类型萃取 std::common_type<>作为返回类型的默认:

#include 
template>
RT max (T1 a, T2 b)
{
return b < a ? a : b;
}

 最好的方法是第二个 : 使用auto。

1.5 函数模板的重载 

可以专门为 max()实现一个可以显式指定返回值类型的模板:

template
auto max (T1 a, T2 b)
{
return b < a ? a : b;
}
template
RT max (T1 a, T2 b)
{
return b < a ? a : b;
}

auto a = ::max(4, 7.2); // uses first template
auto b = ::max(7.2, 4); // uses second template

但是像下面这样调用的话:

auto c = ::max(4, 7.2); // ERROR: both function templates match

两个模板都是匹配的,这会导致模板解析过程不知道该调用哪一个模板,从而导致未知错误。 因此当重载函数模板的时候,你要保证对任意一个调用,都只会有一个模板匹配。

通常而言,在重载模板的时候,要尽可能少地做改动。你应该只是改变模板参数的个数或者显式的指定某些模板参数。 否则,可能会遇到意想不到的问题。

 比如,如果你实现了一个按引用传递的 max()模板,然 后又重载了一个按值传递两个 C 字符串作为参数的模板,你不能用接受三个参数的模板来计 算三个 C 字符串的最大值:

以下代码vs2022不报错但是有警告:
警告    C4172    返回局部变量或临时变量的地址    

#include 
// maximum of two values of any type (call-by-reference)
template T const& max (T const& a, T const& b)
{
return b < a ? a : b;
}
// maximum of two C-strings (call-by-value)
char const* max (char const* a, char const* b)
{
return std::strcmp(b,a) < 0 ? a : b;
}
// maximum of three values of any type (call-by-reference)
template
T const& max (T const& a, T const& b, T const& c)
{
return max (max(a,b), c); // error if max(a,b) uses call-by-value
}
int main ()
{
auto m1 = ::max(7, 42, 68); // OK
char const* s1 = "frederic";
char const* s2 = "anica";
char const* s3 = "lucas";
auto m2 = ::max(s1, s2, s3); //run-time ERROR
}

确保在调用某个函数模板之前,编译器已经看到了相对应的模板定义。

#include 
// maximum of two values of any type:
template
T max (T a, T b)
{
std::cout << "max() \n";
return b < a ? a : b;
}
// maximum of three values of any type:
template
T max (T a, T b, T c)
{
return max (max(a,b), c); // uses the template version even for ints
} //because the following declaration comes
// too late:
// maximum of two int values:
int max (int a, int b)
{
std::cout << "max(int,int) \n";
return b < a ? a : b;
}
int main()
{
::max(47,11,33); // OOPS: uses max() instead of max(int,int)
}

你可能感兴趣的:(开发语言,c++)