auto和decltype

auto 指定符(C++11 起):

  • 对于变量,指定其类型将从其初始化器自动推导而出。
  • 对于函数,指定其返回类型是尾随的返回类型或将从其 return 语句推导出 (C++14 起)。
  • 对于非类型模板形参,指定其类型将从参数推导出。 (C++17 起)

语法
auto variable initializer           (1) (C++11 起)
auto function -> return type        (2) (C++11 起)
auto function                       (3) (C++14 起)
decltype(auto) variable initializer (4) (C++14 起)
decltype(auto) function             (5) (C++14 起)
auto ::                             (6) (概念 TS)
cv(可选) auto ref(可选) parameter    (7) (C++14 起)
template < auto Parameter >         (8) (C++17 起)
cv(可选) auto ref(可选) [ identifier-list ] initializer ;                          
                                    (9) (C++17 起)

解释
1) 在块作用域、命名空间作用域、循环初始化语句等中声明变量时,关键词 auto 可用作类型指定符。
只要初始化器的类型被确定,则编译器会用来自函数调用的模板实参推导规则所决定的类型替换关键词 auto (细节见模板实参推导)。关键词 auto 可与修饰符组合,如 const 或 & ,它们将参与类型推导。例如,给出

const auto& i = expr;
i 的类型恰是虚构模板

  template<class U>
  void f(const U& u)

中参数 u 的类型,倘若函数调用 f(expr) 被编译。从而, auto&& 可根据初始化器被推导成左值或是右值引用,这被用于基于范围的for循环。
若 auto 被用于声明多个变量,则推导类型必须相符。例如,声明

auto i = 0, d = 0.0;

为病式,而声明

 auto i = 0, *p = &i;   

为良式,这里 auto 被推导成int。
2) 在使用尾随返回类型语法的函数声明中,关键词 auto 不进行自动类型检测。它只作为语法的一部分起作用。

template<typename It>
auto fun(It beg,It end)->decltype(*beg)
{
    //处理序列
    return *beg;//返回序列中一个元素的引用
    //引用从来就是作为所指对象的同义词存在,只有在decltype处
    //是个列外,会以引用类型存在
}//位置返回类型允许我们使用参数列表中的参数作为返回类型

3) 在不使用尾随返回类型语法的函数声明中,关键词 auto 指示返回类型将通过其 return 语句的运算数,用模板实参推导规则推导出。

auto fun()
{
...//由return给出
}

4) 若变量的声明类型是 decltype(auto) ,则关键词 auto 被替换成其初始化器的表达式(或表达式列表),而实际类型以 decltype 规则推导。

auto a=1+2;              // a的类型是int
decltype(auto) c1 = a;   // c1 的类型是 int ,保有 a 的副本
decltype(auto) c2 = (a); // c2 的类型是 int& ,为 a 的别名

5) 若声明函数返回类型为 decltype(auto) ,则关键词 auto 被替换成其返回语句的运算数,而其实际返回类型以 decltype 规则推导。
6) 拥有形式 auto:: 的嵌套名称指定符是占位符,它会被类或枚举类型遵循制约类型占位符推导规则替换。
7) lambda 表达式中的参数声明。 (C++14 起)函数参数声明。 (概念 TS)
8) 若模板形参声明为 auto ,则其类型由对应使用参数推导出。
9) 结构化绑定声明

汇总例子:

#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 不能从花括号初始化器列表推导
{
    return {n, n};
}

int main()
{
    auto a = 1 + 2;            // a 的类型是 int
    auto b = add(1, 1.2);      // b 的类型是 double
    static_assert(std::is_same_v<decltype(a), int>);
    static_assert(std::is_same_v<decltype(b), double>);

    decltype(auto) c1 = a;   // c1 的类型是 int ,保有 a 的副本
    decltype(auto) c2 = (a); // c2 的类型是 int& ,为 a 的别名
    std::cout << "a, before modification through c2 = " << a << '\n';
    ++c2;
    std::cout << "a, after modification through c2 = " << a << '\n';

    auto [v, w] = f<0>(); // 结构化绑定声明

    auto d = {1, 2}; // OK : d 的类型是 std::initializer_list<int>
    auto n = {5};    // OK : n 的类型是 std::initializer_list<int>
//  auto e{1, 2};    // C++17 起错误,之前为 std::initializer_list<int>
    auto m{5};       // OK : C++17 起 m 的类型为 int ,之前为 initializer_list<int>
//  decltype(auto) z = { 1, 2 } // 错误: {1, 2} 不是表达式

    // auto 常用于无名类型,例如 lambda 表达式的类型
    auto lambda = [](int x) { return x + 3; };

//  auto int x; // 于 C++98 合法, C++11 起错误
//  auto x;     // 于 C 合法,于 C++ 错误
}

运行结果:
a, before modification through c2 = 3
a, after modification through c2 = 4


decltype 指定符:检查实体的声明类型或表达式的类型及值分类。
语法:

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

解释
1) 若参数是无括号的 id 表达式并指名结构化绑定,则 decltype 产出被引用类型(描述于结构化绑定声明的规定)。
(C++17 起)
2) 若参数为无括号的 id 表达式或无括号的类成员访问表达式,则 decltype 产出以此表达式命名的实体的类型。若无这种实体,或该参数命名一些重载函数,则程序为病式。
3) 若参数是任何其他类型为 T 的表达式,且
a) 若 expression 的值类别为亡值,则 decltype 产出 T&& ;
b) 若 expression的值类别为左值,则 decltype 产出 T& ;
c) 若 expression 的值类别为纯右值,则 decltype 产出 T 。
若 expression 是一个返回类类型纯右值的函数调用,或一个右运算数为这种调用的逗号表达式,则不会对此纯右值引入 (C++17 前)实质化为 (C++17 起)临时对象。类类型不需要完整或拥有可用的析构函数。此规则不应用于子表达式: decltype(f(g())) 中, g() 必须有完整类型,但 f() 不必。
注意若对象名称被括号括着,则它被当做通常的左值表达式,从而 decltype(x) 和 decltype((x)) 通常是不同的类型。
decltype 在声明难以或不可能以标准记号声明的类型时适用,例如 lambda 相关类型或依赖于模板形参的类型。

example:

#include <iostream>

struct A { double x; };
const A* a = new A{0};

decltype(a->x) y;       // y 的类型是 double (声明类型)
decltype((a->x)) z = y; // z 的类型是 const double& (左值表达式)

template<typename T, typename U>
auto add(T t, U u) -> decltype(t + u) // 返回类型依赖于模板形参
{
    return t+u;
}

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

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

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

    decltype(f) g = f; // lambda 的类型是独有且无名的
    i = f(2, 2);
    j = g(3, 3);

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

输出:

i = 33, j = 66
i = 4, j = 9
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值