1. 程式人生 > 實用技巧 >C++中的auto,decltype,typedef,using以及typename

C++中的auto,decltype,typedef,using以及typename

目錄

auto

值與指標等推導

簡單的東西大家都懂,這裡相當於拾遺

const int a = 10;
auto b = a;				// b為 non-const int型別
const const cb = a;		// 明確指明const

auto一般情況下會忽略頂層const,保留底層const(頂層const:指標本身是常量,底層const

:指標所指物件是常量)

int* const apc = &a;
const int* acp = &a;
auto c = apc;		// c為 int*型別, 頂層const被忽略
auto c = acp;		// c為 const int*型別,底層const被保留

如果像是acpc這種縫合怪使用auto推匯出來的是什麼型別大家應該都能猜到吧

返回值推導

C++11中加入了trailing return type(尾返回型別),使用auto將返回值型別後置

template<typename T1, typename T2>
auto MathPlus(T1 a, T2 b) -> decltype(a + b)
{
    return a + b;
}

C++14中,auto的使用更進一步,可以直接推導函式返回值

template<typename T1, typename T2>
auto MathPlus(T1 a, T2 b) { return a + b; }
int main()
{
    std::cout << MathPlus(1 + 2.34) << std::endl;
}

std::initializer_list的推導

此功能需要開啟 /std:c++17

C++14中

auto a{ 1, 2, 3 };		//std::initializer_list
auto b{ 1 };			//std::initializer_list

C++17中

auto a{ 1, 2, 3 };		//非法
auto b{ 1 };			//int
auto c = { 1, 2, 3 };	//與C++14相同,皆為std::initializer_list
auto d = { 1 };			//與C++14相同,皆為std::initializer_list

Lambda表示式推導

在C++11中,Lambda表示式的引數需要具體的型別宣告

auto MyLambda = [](int a, int b) { return a + b; };

C++14中可以這麼做了

auto MyLambda = [](auto a, auto b){ return a + b; };

這裡再複習以下Lambda表示式的使用,Lambda表示式其實是塊語法糖,其結構如下

[函式物件引數](函式引數列表) mutable throw(型別)->返回值型別 { 函式語句 };
  • 當捕獲的是this時,它與其所在的成員函式有著相同的protectedprivate訪問許可權,且為按引用傳遞

  • 按值捕獲的non-const變數一律無法在Lambda表示式內修改(const就不用說了,在哪都無法修改),mutable關鍵字表示可以修改按值捕獲進來的副本(注意修改的是拷貝而不是值本身)

  • 當明確Lambda表示式不會丟擲異常時,可以使用noexcept修飾

    []() noexcept { /* 函式語句 */ }
    
  • 當Lambda表示式沒有捕獲任何引數時,它可以轉換成為一個函式指標

通用捕獲

C++14中,可在Capture子句,即[ ]中引入並初始化新的變數。這些變數不需再存在於Lambda表示式的封閉範圍內。可以使用任意形式的表示式來初始化,同時表示式會自動推匯出變數的型別。捕獲初始化的順序為從左往右執行

auto unip = std::make_unique<int>(10);
auto lambda = [ptr = std::move(unip)]() { /* ptr... */ }

Constexpr Lambda

同樣的,此功能需要開啟std:c++17

顯式constexpr

auto lambda = [](int num) constexpr { return num + 10; };
int arr[lambda(10)];

隱式constexpr

當Lambda滿足constexpr條件時,會自動隱式宣告其為constexpr。也就是說上面那個例子其實不加constexpr也可以

當Lambda轉換成函式指標時,需要顯式指明函式指標為constexpt

constexpr int(Funcp*)(int) = lambda;
int arr[Funcp(100)];

捕獲 *this

全英參考資料Lambda Capture of *this

同樣的,此功能需要開啟std:c++17

在C++14中

class MyClass
{
private:
    int num = 10;
    int sum(int a, int b) { return a + b; }
public:
    auto MyLambda()
    {
        auto lr = [this]() { num = 100; };							//按引用捕獲
        auto lv = [_Myc = *this]() mutable { _Myc.num = 100; };		//按值捕獲
        lr();
        lv();
    }
};

在C++17中,按值捕獲的編寫無需如此複雜

auto lv = [*this]() mutable { num = 100; };			//按值捕獲

重點:[=]中捕獲進的this是個指標,因此修改時會改變原來的值

auto MyLambda()
{
    int temp = 20;
    auto lv = [=]() { num = 100; temp = 200; };		//編譯器報錯,temp無法修改
}

這裡num可以被修改,且修改的是類中的值本身;temp不可以被修改,因為沒有mutable修飾,即使有mutable,修改的也是捕獲進來的副本,而非值本身

如果不想修改num,只想修改其副本

auto MyLambda() { auto lv = [=, _Myc = *this] mutable { _Myc.num = 100; }; }	//C++14
auto MyLambda() { auto lv = [=, *this] mutable { num = 100; }; }				//C++17

若捕獲的是*this,且想要呼叫成員函式,則需要mutable修飾。雖然感覺很少會這麼用,但是還是寫一下

auto MyLambda() { auto lv = [_Myc = *this] mutable { int value = _Myc.sum(1,2 ); }; }
auto MyLambda() { auto lv = [*this] mutable { int value = sum(1, 2); }; }

Range-base-loop with auto

參考自知乎-藍色-range-base-loop中使用auto

總結:

  • 當你想要拷貝range的元素時,使用for(auto x : range)
  • 當你想要修改range的元素時,使用for(auto&& x : range)
  • 當你想要只讀range的元素時,使用for(const auto& x : range)

template<auto>

不會,摸了