概述
文章目录
- C++ learning notes
- Introduction
- template
- friend declaration
- reference collapsing:
- parameter packet
- specialize
C++ learning notes
Introduction
I write down this succession of notes for the purpose of developing my English ability&habit while learning C++. Therefore I make all the notes in English.
If you have any reap or advice, don’t hesitate to comment(with any language). If you find some bugs in my notes or sentences or grammar, please let me know(granted my English is very poor). If you are not used to reading notes in English, just close this page and browse another note.
template
template <typename T> //key word:template
//<template T> is template parameter list
//template parameters are divided by comma
/*
*moerover, key word typename is the same with key word class in this situation
*/
int compare(const T& v1, const T& v2) {
if (v1 > v2) return -1;
else if (v2 < v1) return 1;
else return 0;
}
//type parameter can be used as return type or parameter type
//it can be used as variables declaration and type translation, as well
template <typename T>
T foo(T* p) {
T tmp = *p;
//omitted details
return T;
}
/*
*we can define nontype parameter and the parameter's value is provided by
*clients or compiler
*in this case, compiler provide nontype parameters int values depends on
*const char array's length
*nontype parameter can be int, object/function pointer or lvalue reference
*/
template<unsigned N, unsigned M>
int compare(const char (&p1)[N], const char (&p2)[M]){
return strcmp(p1,p2);
}
/*
*template can be declared as inline or constexpr
*this declaration should be put between template parameter list and return type
*/
template<typename T> inline T min(const T&, const T&);
principle: template function’s parameters are supposed to be const reference, which guarantees non-copyable object’s availability.
if we want to define a class member function out of the class body, it’s type should be:
template <typename>
return-type class-name::member-name(parameter-list) {function-bod}
By default, template class’ member function is instantiated when it is called, which help us to use a template class even though there are some member function incompatible with a type, we can use part of class functions.
friend declaration
//friend declaration
template <typename T> class Pal;
class C {
friend class Pal<C>; //Pal instantiated by class C is a friend
template <typename T> friend class Pal2; //each instantiated Pal2 is C's friend
};
template <typename T> class C2 {
friend class Pal<T>;
template <typename X> friend class Pal2;//each instantiated Pal2 is each instantiated C2's friend
//notice: friend's typename should be different from template class'
friend class Pal3; //Pal3 is a non-template class, every instantiated C2 is Pal3's friend
};
//let template's typename as friend
template <typename T>class C3 {
friend T; //type T is friend of C3 instantiated by T
};
if a template class contains static members, each type instantiated class has its own static members.
if we access a type name from a template class, we need to use the key word typename to explicitly ask compiler treats the name as a type name.
we can define default template argument, like code below.
template <typename T, typename F = less<T>>
int compare(const T& v1, const T& v2, F f=F()){
if (f(v1,v2)) return -1;
if (f(v2,v1)) return 1;
return 0;
}
//even if all the parameters of a template's typename parameter list is default argued
//we have to use angle brackets to ask compiler instantiate a class from a template
template <typename T = int> class tmp_array{/*details*/};
tmp_array<> a;
Besides, it is known that once we define a template class’ type, compiler instantiates a class instance. But this activity faces an embarrassing situation. Once we create a project and use template class with the same type in different file, the compiler will instantiates several instances in each file. Which may be a mass overhead. We can use the key word extern to declare a class to prevent from such overhead.
extern template class Blob<string>;
//declaration
template int compare(const int&, const int&); //definition
//in this case, compiler will instantiates the whole members
//thus the type must apply to every member
int main() {
Blob<string> sa1, sa2; //compiler instantiates class in other file
//Thus key word extern expresses that there is a definition in our project
}
and if we can’t make sure what the return type definitely is, we can use the tail-set return to declare function’s return type
//using tail-set return to explicitly declare function's return type
template <typename It>
auto fcn(It beg, It end) -> decltype(*beg) {
//details
return *beg;
}
reference collapsing:
X& &, X& &&, X&& & will collapse to X&
X&& && will be collapse to X&&
By define a parameter as an rvalue reference, we can save all of argument’s type-info. By using reference, we can save the const-info. By define parameters as rvalue references, we can use reference collapsing to save arguments’ reference characteristic.
template <typename F,typename T1, typename T2>
void flip(F f, T1 &&t1, T2 &&t2) {
f(t1, t2);
}
However, function expression returns an lvalue, which restrict us from binding an rvalue reference to an rvalue reference. In this case, we need to use the lib function std::forward, which is defined in lib utility, to save the reference type(by using the reference collapsing). Thus, we modifies the original code to the below one
template <typename F,typename T1, typename T2>
void flip(F f, T1 &&t1, T2 &&t2) {
f(std::forward<T1>(t1), std::forward<T2>(t2));
}
When we overload a template function, compiler will chose the more specialized one. Thus if we define a template function
template <typename T> string func(T &a) {/*details*/}
template <typename T> string func(T *a) {/*details*/}
and we hope the compiler may cope with char & const char as string, we need to define two specialized overloaded function
string func(const string&); //declare a string type instance to avoid from default call
string func(char *p) {return func(string(p));}
string func(const char *p) {return func(string(p));}
parameter packet
we use symbol … after keyword typename or class to express indefinite number of parameters
//Args is a template parameter packet, rest is a function parameter packet
//Args means zero or several template parameters
//rest means zero or several function parameters
//types in packet can be different from each other
template <typename T, typename... Args>
void foo(const T &t, const Args&... rest){
cout<<T<<"t";
print(cout, rest)...;
return;
}
we can use sizeof to calculate the number of parameters in the packet
call packet parameters:
//end the recursion and print the last element
template <typename T>
ostream &print(ostream &os, const T &t) {
return os << t;
}
//recursion call
template <typename T, typename... Args>
ostream &print(ostream &os, const T &t, const Args&... rest) {
os << t << "t";
return print(os, rest...);
}
int main() {
int i = 1;
double d = 2.0;
long l = 321;
string s = "hello world!";
foo(cout, i, d, l, s);
return 0;
}
//output:1
2
321
hello world!
specialize
//specialize
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(&)[M] is a reference to an char array
//a specialized instance of function compare
template <>
int compare(const char *const &, const char *const &);//a reference to an const (or not) pointer
notice: Once we need a specialized instance but forget to declare or define one, where there is a template suits to this instance’s type, the compiler will instantiate an instance. This mistake won’t be checked up. Thus we are supposed to define templates with the same name in the same header file. And put the specialized instances after those template. Certainly we need to check up whether we have declared an instance or not.
最后
以上就是欣喜季节为你收集整理的C++ Primer learning notes #3C++ learning notes的全部内容,希望文章能够帮你解决C++ Primer learning notes #3C++ learning notes所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复