警告
本文最后更新于 2024-02-03 ,文中内容可能已过时。
CS106L 中关于 Templates 的部分
template 应该算得上很自然的想法(🤔 又或者是套娃的另一次应用),我认为这就是对函数的进一层抽象,它将函数的逻辑抽象成与类型无关,比如
1
2
3
4
template < typename T >
T min ( T a , T b ) {
return ( a < b ) ? a : b ;
}
1
2
3
4
template < typename Type = int >
Type myMin ( Type a , Type b ) {
return a < b ? a : b ;
}
这里的 typename
没有指明类型,实际上可以写成 class T
,这样这个函数就不会接受 int 之类的类型。那个 =int
表示其默认类型(虽然我还没认识到写它的意义)。
可以针对性的再写一个特定类型的模板函数:
1
2
3
4
5
6
7
8
9
10
11
template <> void print_msg < float > () {
std :: cout << "print_msg called with float type! \n " ;
}
template < bool T > int add3 ( int a ) {
if ( T ) {
return a + 3 ;
}
return a ;
}
1
auto minvar = min < int > ( 1 , 2 );
隐式存在一个问题在于参数的类型未必能被识别出来(有些类型的定义方式差不多)。不过貌似编译器这时候会报错。
从一个实际的类型推广到一个模板,这个过程被称为 Concept Lifting 。对于隐式类型的来说,这种提升可能会导致传入一些不可以工作的类型(比如函数内部使用了 =
赋值,但 stream 是不可以这样做的)
毕竟有了函数指针,其实可以把抽象做的更细一些。比如 Predicate Functions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template < typename InputIt , typename UniPred >
int count_occurrences ( InputIt begin , InputIt end , UniPred pred ) {
int count = 0 ;
for ( auto iter = begin ; iter != end ; ++ iter ) {
if ( pred ( * iter )) count ++ ;
}
return count ;
}
bool isVowel ( char c ) {
std :: string vowels = "aeiou" ;
return vowels . find ( c ) != std :: string :: npos ;
}
std :: string str = "Xadia" ;
count_occurrences ( str . begin (), str . end (), isVowel );
C++20 允许开发者显示指定其 template 类型的要求,具体可以参见文档:Constraints and concepts (since C++20) 和 Requires expression (since C++20)
Lamda function:
1
2
3
auto func = [ capture - clause ]( parameters ) -> return - value {
// body
}
C++14 开始,这个 return-value
是可选的。
1
2
3
4
5
6
7
[] // captures nothing
[ limit ] // captures lower by value
[ & limit ] // captures lower by reference
[ & limit , upper ] // captures lower by reference, higher by value
[ & , limit ] // captures everything except lower by reference
[ & ] // captures everything by reference
[ = ] // captures everything by value
1
2
auto isMoreThan = [ limit ] ( int n ) { return n > limit ; };
isMoreThan ( 6 ); //true
有了这个之后,也就不需要像之前那样定义Predicate Functions 了,可以直接写 lamdba。
STL 的一些 algorithm 不能用于开发者自定义的类型(比如寻找最小值之类的),这时候需要用到 lambda 函数。
比如对于这样的 vector:
1
std :: vector < Student > vecstu {{ 1 , 2 , 3.0 }, { 2 , 2 , 5.0 }};
直接使用 std::minmax_element()
是无法通过编译的
1
auto [ min , max ] = std :: minmax_element ( vecstu . begin (), vecstu . end ());
额,根据我看到的录像那里,其开发环境是没有在编译前给出预警的。但是我的 vscode 在只给了两个参数的时候:
1
In template: invalid operands to binary expression ('Student' and 'Student') clang(typecheck_invalid_operands)
这时候就可以加一个 lamdba 函数,并传给 minmax_element()
1
2
3
auto compareStudent = []( Student & s1 , Student & s2 ){
return s1 . averge < s2 . averge ;
};
1
auto [ min , max ] = std :: minmax_element ( vecstu . begin (), vecstu . end (), compareStudent );
在 std::copy
这个函数中,如果传入的 iterator 指向的 container 没有足够的空间,那么就会复制到为初始化的内存中,这时候应该传入一个 iterator adaptor。这种函数可以给 iterator 加点料(比如 back_inserter()
会让返回的 iterator 在赋值不存在的空间时扩展 container)。
引用上一章一开始给出的代码:
1
2
3
4
5
6
int main () {
std :: vector < int > vec ( 20 );
std :: generate ( vec . begin (), vec . end (), rand );
std :: sort ( vec . begin (), vec . end ());
std :: copy ( vec . begin (), vec . end (), std :: ostream_iterator < int > ( cout , " \n " ));
}