1. 程式人生 > >[C/C++11]_[初級]_[關於auto和decltype說明符的簡單介紹]

[C/C++11]_[初級]_[關於auto和decltype說明符的簡單介紹]

場景

1.auto,decltype說明符是C++11新增的型別推導(deduction)說明符, 他們都有各自的使用場景, 作用相互補充.

2.搞清楚它們的使用規則對用好這兩個說明符能讓你用的更加正確; 還有你會發現C++原來那麼複雜的其中一個原因就是它的說明符根據表示式,符號(*,&,…)使用情況,環境相當複雜多變,要記住這些區別非常不容易, 單單是這些符號的組合就讓人頭疼, 做Java的或C的已經相當幸福了, 至少目前我的水平也覺得C++的說明符, 符號的組合實在太多了.

3.要熟練使用還是得搞清楚lvalue,rvalue,xvalue, lvalue expression, rvalue expression, xvalue expression.

說明

auto

語法

auto variable initializer
auto function -> return type

1.第一種是變數型別說明符, 在編譯時根據初始化器的型別來推導variable的實際型別. 可以使用 auto推導的實際型別, 或者auto& 來表示引用型別, auto&& 來表示一個左值或右值引用. 其中 auto 還可以用與lambda表示式的宣告.

auto i = 0, *p = &i;
auto lambda = [](int x) { return x + 3; };

2.第二種宣告一個帶->拖尾返回符號的函式. auto不會執行自動函式探測(detect), 僅僅是作為語法的一部分.

template<typename T, typename U>
auto add(T t, U u) -> decltype(t + u) // return type depends on template parameters
                                      // return type can be deduced since C++14
{
    return t+u;
}

decltype

語法

decltype ( entity ) (1) (since C++11)
decltype ( expression )
(2) (since C++11)

1.第一種情況, 推導這個型別,可以說是變數的型別.

int i  = 0;
decltype(i) j;
decltype((i)) j1 = j; // 帶括號推匯出引用型別.

auto my_hHash = [](HCRYPTHASH* hHash){CryptDestroyHash(*hHash);};
std::unique_ptr<HCRYPTHASH,decltype(my_hHash)> p2(&hHash,my_hHash);

2.第二種情況是表示式, 注意和auto不同的是它支援表示式;
– 如果表示式的值型別是 xvalue(過期值), 那麼decltype 推導(yields)出T&&
– 如果表示式的值型別是 lvalue(左值), 那麼decltype 推導(yields)出T&
– 如果表示式的值型別是 prvalue(純右值), 那麼decltype 推導(yields)出T


// 值型別是xvalue.
int i = 2;
decltype(std::move(i)) a1 = std::move(2);

// 值型別是左值
int j = 2;
decltype(i = 3) j3 = j;

// 值型別是純右值
decltype(a->x) y;     
decltype((a->x)) z = y;

template<typename T, typename U>
auto add(T t, U u) -> decltype(t + u)

{
    return t+u;
}

例子

#include <regex>
#include <assert.h>
#include <iostream>
#include <iterator>
#include <set>
#include <typeinfO>

struct A { double x; };
const A* a;

decltype(a->x) y;       // type of y is double (declared type)
decltype((a->x)) z = y; // type of z is const double& (lvalue expression)

template<typename T, typename U>
auto add(T t, U u) -> decltype(t + u) // return type depends on template parameters
                                      // return type can be deduced since C++14
{
    return t+u;
}

// https://stackoverflow.com/questions/28621844/is-there-a-typeid-for-references
template <class T>
std::string
type_name()
{
    typedef typename std::remove_reference<T>::type TR;
    std::unique_ptr<char, void(*)(void*)> own
           (
#ifndef _MSC_VER
                abi::__cxa_demangle(typeid(TR).name(), nullptr,
                                           nullptr, nullptr),
#else
                nullptr,
#endif
                std::free
           );
    std::string r = own != nullptr ? own.get() : typeid(TR).name();
    if (std::is_const<TR>::value)
        r += " const";
    if (std::is_volatile<TR>::value)
        r += " volatile";
    if (std::is_lvalue_reference<T>::value)
        r += "&";
    else if (std::is_rvalue_reference<T>::value)
        r += "&&";
    return r;
}

void TestDecltype() 
{
    int i = 33;
    decltype(i) j = i * 2;

    std::cout << "i = " << i << ", "
              << "j = " << j << '\n';

    auto f = [](int a, int b) -> decltype(a + b)
    {
        return a * b;
    };

    decltype(f) g = f; // the type of a lambda function is unique and unnamed
    i = f(2, 2);
    j = g(3, 3);

    decltype((j)) j1 = j;
    j1 = 100;
    auto& j2 = j1;

    auto i2 = add(10,2);

    decltype(i = 3) j3 = j;

    decltype(std::move(i)) a1 = std::move(2);

    std::cout << "i = " << i << ", "
              << "j1 = " << j1 <<  ", "
              << "j2 = " << j2 <<  ", "
              << "i2 = " << i2 <<  ", "
              << "j3 type = " << typeid(j3).raw_name()   <<  ", "
              << "j3 = " << j3 <<  ", "
              << "j3 type = " << type_name<decltype(j3)>() <<  ", "
              << "decltype(std::move(a) = " << type_name<decltype(std::move(i))>() <<  ", "
              << "j = " << j << '\n';
}

輸出:

i = 33, j = 66
i = 4, j1 = 100, j2 = 100, i2 = 12, j3 type = .H, j3 = 100, j3 type = int&, decl
type(std::move(a) = int&&, j = 100

參考

C++11_初級_左值引用宣告和右值引用宣告
C/C++不常見語法特性初級左值-右值-lvalue-rvalue
auto specifier (since C++11)
decltype specifier (since C++11)
is-there-a-typeid-for-references