[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