靜態類型和動態類型的區別
- 靜態類型的類型檢查主要發生在編譯階段
- 動態類型的類型檢查主要發生在運行階段
關鍵字auto
傳統auto是一個存儲類型指示符(storage-class-specifier),使用auto修飾的變量是具有自動存儲期的局部變量,而C++11中auto是作爲一個新的類型指示符(type-specifier),它聲明的變量的類型必須由編譯器在編譯時期推導而得。
int main()
{
double foo();
auto x = 1; // x的類型爲int
auto y = foo(); // y的類型爲double
struct m { int i; }str;
auto str1 = str; // str1的類型是struct m
auto z; // 無法推導,無法通過編譯
z = x;
}
值得注意的是變量z,這裏我們使用auto關鍵字來“聲明”z,但不立即對其進行定義,此時編譯器則會報錯。這跟通過其他關鍵字先聲明後定義變量的使用規則不同,auto聲明的變量必須被初始化,以使編譯器能夠從其初始化表達式中推導出其類型。從這個意義上來講,auto並非一種“類型”聲明,而是一個類型聲明時的“佔位符”,編譯器在編譯時期會將auto替代爲變量實際的類型。
auto優勢
-
在擁有初始化表達式的複雜類型變量聲明時簡化代碼
void loopover(std::vector<std::string> & vs) { std::vector<std::string>::iterator i = vs.begin(); // iterator,想說愛你並不容易 for (; i < vs.end(); i++) { // 一些代碼 } }
以上代碼可以優化爲:
void loopover(std::vector<std::string> & vs) { for (auto i = vs.begin(); i < vs.end(); i++) { // 一些代碼 } }
-
可以免除程序員在一些類型聲明時的麻煩,或者避免一些在類型聲明時的錯誤
-
其“自適應”性能能夠在一定程度上支持泛型的編程
template<typename T1, typename T2> double Sum(T1 & t1, T2 & t2) { auto s = t1 + t2; // s的類型會在模板實例化時被推導出來 return s; } int main() { int a = 3; long b = 5; float c = 1.0f, d = 2.3f; auto e = Sum(a, b); // s的類型被推導爲long auto f = Sum(c, d); // s的類型被推導爲float }
-
auto還會在一些情況下取得意想不到的好效果
#define Max1(a, b) ((a) > (b)) ? (a) : (b) #define Max2(a, b) ({ \ auto _a = (a); \ auto _b = (b); \ (_a > _b) ? _a: _b; }) int main() { int m1 = Max1(1*2*3*4, 5+6+7+8); int m2 = Max2(1*2*3*4, 5+6+7+8); }
相比傳統的三元運算符表達式,Max2中將a和b都先算出來,再使用三元運算符進行比較,提高了計算性能。
auto使用細則
-
auto類型指示符與指針和引用之間的關係
int x; int * y = &x; double foo(); int & bar(); auto * a = &x; // int* auto & b = x; // int& auto c = y; // int* auto * d = y; // int* auto * e = &foo(); // 編譯失敗, 指針不能指向一個臨時變量 auto & f = foo(); // 編譯失敗, nonconst的左值引用不能和一個臨時變量綁定 auto g = bar(); // int auto & h = bar(); // int&
上文中可以看出,對於a,c,d三個變量而言,聲明其爲auto *或auto並沒有區別,而如果要使得auto聲明的變量是另一個變量的引用,則必須要使用auto&,如同本例中的變量b和h一樣。
-
auto與volatile和const之間也存在着一些相互的聯繫
C++11 標準規定auto可以與cv限制符一起使用,不過聲明爲auto的變量並不能從其初始化表達式總“帶走”cv限制符。double foo(); float * bar(); const auto a = foo(); // a: const double const auto & b = foo(); // b: const double& volatile auto * c = bar(); // c: volatile float* auto d = a; // d: double auto & e = a; // e: const double & auto f = c; // f: float * volatile auto & g = c; // g: volatile float * &
其中變量d,f卻無法帶走a和f的常量性或者易失性。不過聲明爲引用的變量e,g都保持了其引用的對象相同的屬性(事實上,指針也是一樣的)。
auto無法使用的情況
auto受制於語法的二義性,或者是實現的困難性,auto往往也有使用上的限制。
void fun(auto x =1){} // 1: auto函數參數,無法通過編譯
struct str{
auto var = 10; // 2: auto非靜態成員變量,無法通過編譯
};
int main() {
char x[3];
auto y = x;
auto z[3] = x; // 3: auto數組,無法通過編譯
// 4: auto模板參數(實例化時),無法通過編譯
vector<auto> x = {1};
}
- 從fun函數可以看出,auto是不能做形參的類型的
- 編譯器阻止auto對結構體中的非靜態成員進行推導,即使成員擁有初始值
- 聲明auto數組。x是一個數組,y的類型是可以推導的,是一個char數組類型,而聲明auto z[3]這樣的數組同樣會被編譯器禁止
- 在實例化模板的時候使用auto作爲模板參數,雖然可以一眼而知是int類型,但編譯器卻阻止了編譯