场景
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