一.decltype说明符
1.1.语法:
decltype ( entity ) 实体
decltype ( expression )//返回 expression 表达式的类型-必须加括号
1.2.用途:
1)在声明使用标准符号难以或无法声明的类型时使用
(如与lambda相关的类型或依赖模板参数的类型)与auto一样用于进行编译时类型推导
2)检查实体的声明类型或表达式的类型和值类别,函数返回值后置推导
3)auto只能用于赋值语句,decltype()可推导任意表达式类型,但是必须在()写全表达式
故而引申出decltype(auto)例:decltype(auto) z =x+y == decltype(x + y) dest =x+y;
1.3.说明:
1) decltype 仅“查询”表达式类型不会对表达式进行“求值”
2)auto声明一变量;decltype则可从变量或表达式中得到类型
3)类型不必是完整的或具有可用的析构函数且可是抽象的
该规则不适用子表达式:decltype(f(g())); //g()必须具有完整的类型,但是f()不需要
1.4.注意:
1)如对象名称带有括号则将其视为普通的左值表达式
因此decltype(x)和decltype((x))通常是不同的类型
2)字符串字面值常量是个左值,且是const左值,而非字符串字面值常量则是个右值
3)函数:
std::is_lvalue_reference<>::value ;//判断表达式是否为左值
std::is_rvalue_reference<>::value ;//判断表达式是否为右值
1.5.推导规则:
1)如参数是无括号(id-expression表达式或类成员访问表达式),则返回表达式实体的类型
如没有这样实体,或参数指定是重载函数则程序格式错误
如参数是未括号id-expression命名结构化绑定,则返回引用类型(C++ 17)
如参数是未括号id-expression命名非类型模板参数,则返回模板参数类型C++ 20)
2)如expression是函数调用或重载操作符调用返回函数的返回数值的类型
3)如参数是表达式类型T,则
a)如表达式值类别是xvalue(亡值)则返回T&&;
b)如表达式值类别为lvalue(左值)则返回T&;
c)如表达式值类别为prvalue(纯右值)则返回T
2.实例:
实例1:
typename T::const_iterator it;
decltype(T.begin()) it;
typedef decltype(nullptr) nullptr_t; //标准库中应用
typedef decltype(sizeof(0)) size_t;
实例2:
#include <iostream>
#include <assert.h>
using namespace std;
void foo(int x) {};
void foo(char c1, char c2) {}; //重载函数
const bool bar(int x) { return true; };
const bool& func(int x) { return true; };
struct Data { double d; }s;
//返回类型后置语法
template<typename T, typename U>
auto add(T t, U u) -> decltype(t + u) //返回类型取决于模板参数
{return t + u;}
int main() {
//规则一:推导为表达式类型
int x = 1;
decltype(x) z1 = x * 2; //int z1;
int arr[4] = { 0 };
int* ptr = arr;
decltype(ptr) z2 = &x; //int * z2; 标记符表达式
decltype(arr) z3 = { 0 }; //int[4] 标记符表达式 int z3[4];
const Data* p_s = &s;
decltype(p_s->d) z4=0; //doubel z4 成员访问表达式
decltype((p_s->d)) z5=0; //const double& z5(左值表达式)
//规则二:推导为函数调用返回类型
//对于纯右值只有类类型可携带CV限定符其他一般忽略掉CV限定符
decltype(bar(1)) b1 = true; //bool 因为函数返回的是纯右值
decltype(func(1)) b2 = true; //const bool &
//decltype(foo(x)) y; //重载函数-编译错误
auto _mul = [](int a, int b) -> int {return a * b; };
decltype(_mul) mul = _mul; //lambda函数的类型是独特的和未命名
assert(mul(2, 3) == _mul(2, 3));
assert(add(2, 3) == 5);
//规则三:左值-推导为类型的引用
decltype((x))y1 = x; //int& - (x)是一个左值
decltype(true ? x : x) y2 = x; //int& 条件表达式返回左值
decltype(++x) y3 = x; //int& ++i返回i的左值
decltype(arr[5]) y4 = x; //int& []操作返回左值
decltype(*ptr)y5 = x; //int& *操作返回左值
decltype("hello")y6 = "hello"; //const char(&)[6] 字符串字面常量为const左值
//右值,则推导为本类型
decltype(1) y7 = 10; //int
decltype(x++) y8 = x; //int x++返回右值
}
二. decltype(auto)占位符类型说明符
1.1.用途:
1)对于变量,指定将从其初始值设定项自动推断出要声明的变量的类型 C++11
2)对于函数,指定将从其return语句推导返回类型C++14
3)对于非类型模板参数,指定将从参数推导出类型C++17 template<auto I> struct A;
1.2.格式:
auto -1 (since C++11) 存储类
decltype(auto) -2 (since C++14)
type-constraint auto -3 (since C++20)
type-constraint decltype(auto) -4 (since C++20)
说明:
1)type-constraint -可选,类型约束,后可跟跟<>中模板参数列表
占位符auto可附带修饰符(如const or & )将参与类型推演
2)1,3使用模板参数推导规则推导类型
3)2,4类型是decltype(expr),其中expr是初始值设定项
1.3.使用:
1)指定变量类型:
auto x = expr;//从初始化程序推导
//decltype(auto)从函数调用模板参数推导变量类型
template<class U> void f(const U& u)
const auto& i = expr;
f(expr);
//auto&& 用于ranged for循环(基于左值或右值)
//如用占位符类型说明符声明多个变量,则推导类型必须匹配
auto i = 0, d = 0.0; //格式错误
auto i = 0, *p = &i; //格式正确,auto可以推导为整型
auto f() -> int, i = 0; //声明中混合变量和函数错误
2)type-id x;类型从初始化程序推导
T x;类型从初始化程序(小括号)或初始化列表推导(大括号)
3)Lambda表达式或函数参数为auto(since C++20)
[](auto&&){}; //通用lambda
void f(auto); //函数声明引入了一缩写函数模板
//auto 说明符可与后跟尾随返回类型的函数声明符一起使用
auto (*p)() -> int; //将p声明为返回int
auto (*q)() -> auto = p; //将q声明为指向返回T的函数的指针-从p的类型推导出T
4)非类型模板参数
template<auto I> struct A;
说明:
auto说明符也可以在结构化绑定声明中使用
auto关键字也可以在嵌套名称说明符中使用
2.实例:
#include <iostream>
#include <assert.h>
using namespace std;
#include <iostream>
#include <utility>
template<class T, class U>
auto add(T t, U u) { return t + u; } //返回类型是operator+(T, U)的类型
// 函数调用必须使用decltype(Auto),以防它调用的函数通过引用返回
template<class F, class... Args>
decltype(auto) PerfectForward(F fun, Args&&... args)
{ return fun(std::forward<Args>(args)...);}
template<auto n> // C++17 auto 参数声明
auto f() -> std::pair<decltype(n), decltype(n)> // auto不支持从init-list推断
{ return { n, n };}
int main() {
auto x = 1 + 2;
auto y = add(1, 1.2);
static_assert(std::is_same_v<decltype(x), int>);
static_assert(std::is_same_v<decltype(y), double>);
auto z1 = x; // 类型c0是int,持有a的副本
decltype(auto) z2 = x; // 类型z2是int,持有x的x副本
decltype(auto) z3 = (x); // type of z3 is int&, an alias of x < /FONT >
++z3; assert(z3 == 4);< /FONT >
auto [v, w] = f<0>(); //结构化绑定声明 < /FONT >
auto d = { 1, 2 }; // OK: type of d is std::initializer_list<int>
auto n = { 5 }; // OK: type of n is std::initializer_list<int>
auto m{1}; // auto 只能从单个值initializer_list<int>中推导 auto e{1, 2};错误
//decltype(auto) z = { 1, 2 } // Error: {1, 2} is not an expression
// auto通常用于未命名的类型,例如lambda表达式的类型
auto lambda = [](int x) { return x + 3; };< /FONT >
// auto int x; // valid C++98, error as of C++11
// auto x; // valid C, error in C++
}