語法書 / AA 競程語法書 下冊 / 第十二單元 / 用 using 幫型態取別名

12.7 用 using 幫型態取別名

競程整天跟 long long 打交道(上冊 2.9 之後你就知道為什麼),一題打十幾次、九個字母加一個空格,高手嫌煩,於是幫型態取別名

using ll = long long;      // 從此 ll 就是 long long

這行的意思是從此 ll 就是 long long 的另一個名字lllong long同一個型態的兩個名字——不是新型態,就是綽號,跟本名隨時可以混用:

#include <bits/stdc++.h>
using namespace std;

using ll = long long;                 // 兩個競程最常見的別名
using pii = pair<int, int>;

int main() {
    ll big = 1000000000000000000LL;   // 等同 long long big = ...
    cout << big << '\n';

    vector<pii> v = {{3, 4}, {1, 2}};
    cout << v[0].first << ' ' << v[1].second << '\n';

    long long x = big;                // 綽號與本名可混用,同一個型態
    cout << x << '\n';
    return 0;
}

執行結果:

1000000000000000000
3 2
1000000000000000000

pair<int, int> 縮成 pii 之後,vector<pii>vector<pair<int, int>> 清爽得多——開場高手程式碼的第四行就是這一句。

兩件小事:

  • 舊式寫法 typedef:老程式碼裡常見 typedef long long ll;,效果完全相同——只是新舊語法的差異(新名字的位置不同)。自己寫建議用 using:新名字在左邊、等號在中間,跟變數宣告同一個閱讀方向。
  • 也有人寫 #define ll long long:能動,但它跟 using 是兩回事——#define12.9 的巨集,編譯前的純文字取代。詳見下面的對照。
  • using namespace std; 的關係:同一個關鍵字的不同用法。那句是「打開 std 命名空間」(你從第一支程式就在寫),本節是「取型態別名」——別混為一談。

為什麼不用 #define ll long long

兩者都能讓你少打字,差別在誰做這件事#define前置處理器在編譯之前做純文字取代——整份程式只要出現 ll 這個字就被換掉,它不認識作用域,也不管你把 ll 當成什麼在用。using 則是真的宣告一個型態別名,跟一般的宣告一樣遵守作用域

三個本站實測的差別:

情境 #define ll long long using ll = long long;
想拿 ll 當變數名:int ll = 5; 展開成 int long long = 5;,編譯錯誤 expected unqualified-id before '=' token 可以,區域名稱把別名遮蔽掉而已
結構體裡想放個成員叫 ll 編譯錯誤 declaration does not declare anything 可以
寫在某個函式裡面 出了那個函式依然有效(一路影響到檔案結尾) 出了函式就失效,符合一般宣告的直覺

ll 只有兩個字母,很容易跟別人的變數名或成員名撞在一起——而 #define 撞上去就是編譯錯誤,訊息還往往指向一個看起來毫無關係的地方。所以取型態別名一律用 using#define 留給 12.9 真正需要文字取代的場合。

動手試試看:把 12.5 動手試試看的重新排列解答加上 using pii = pair<int, int>; 改寫一遍。行有餘力再試試別名疊別名:using vpii = vector<pii>; 也完全合法——然後想想,縮寫到這個程度,是省力還是已經在寫密碼了?