decltype,decltype(auto) (tcy)

一.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++          
 }       

 

上一篇:C++ 动态库、静态库、__declspec(dllexport)、符号隐藏、gcc visibility (“default“)


下一篇:C++ std::declval <utility> (tcy)