C++11特性之auto

占位符类型说明

对变量而言,指定要声明的变量的类型将从其初始值中自动推导出来。

对于函数而言,指定的返回类型将从其返回语句中推导出来(从C++14开始)

对于非模版类型的参数而言,指定的类型将会从参数中推导出来(从C++17开始)

语法

受约束的类型(可选)auto(从C++11开始)

受约束的类型(可选)decltype(auto)(从C++14开始)

受约束的类型-(从C++20开始),是一个概念名称,可选资格的、可选地跟在一组模版参数列表<>后面

1)使用模版参数推导规则出类型;

2)类型是decltype(expr),其中expr是初始值;

占位符可能伴随着修饰符,例如const,或者,占位符将参与类型推导。&占位符decltype(auto)必须是声明类型的唯一组成部分。(从C++14开始)

解释

占位符类型说明符可能出现在以下背景中:

(1)在一个变量的类型说明符序列中:auto x=expr;作为一个类型说明符。该类型是从初始值中推导出来的。如果占位符类型说明符是或类型约束auto(从C++20开始),则使用函数调用中模板参数推导的规则从初始值中推导变量类型(有关详细信息,请参见模板参数推导-其他上下文)。例如,给定 const auto& i = expr;,那么 i 的类型恰是某个虚构模板 template<class U> void f(const U& u) 中参数 u 的类型(假如函数调用 f(expr) 通过编译)。因此,取决于初始化器,auto&& 可被推导成左值引用或右值引用类型,这被用于基于范围的 for 循环(for循环也适用)。

这个例子的意思是:假如有一个可以编译通过的模版template<class U> void f(const U& u),然后f被调用,实参是expr,即f(expr);再将expr赋给i,即const auto& i = expr;根据expr是左值还右值,auto&&可以被推导为左值引用或右值引用(如果expr是左值,auto&就是auto&,如果expr是右值,auto&则会被推导成auto&&)

如果占位类型说明符是 decltype(auto) 或者是受类型约束 decltype(auto) (从C++20 开始),那么推导出的类型是 decltype(expr),其中 expr 是初始值。(从C++14开始)。

如果占位符类型说明符用于声明多个变量,则推导的类型必须匹配。例如,声明auto i=0,d=0.0;格式不正确,而声明auto i=0,*p=&i;格式匹配,自动推导为int。

(2)new 表达式中的类型标识。从初始推导类型。对于 new T init(其中 T 含占位符类型,而 init 是带括号的初始化器或带花括号的初始化器列表),如同在虚设的声明 T x init; 中对变量 x 一般推导 T 的类型。

解释一下:假如T是一个Base类型,则new T init等价new Base (a)或者new Base(a,  b);(a、b的类型都是Base),还等同于Base x(a)或者Base x(a,b),利用x推导a、b的类型。

(3)(从C++14 开始) 函数或 lambda 表达式的返回类型中:auto& f();。从其未舍弃的 (从C++17 开始) return 语句的操作数推导返回类型。
见返回类型推导:Function declaration - cppreference.com

(4)(从C++17开始) 非类型模板形参的形参声明中:template<auto I> struct A;。从对应的实参推导它的类型。

auto 说明符也可以在显式类型转换的简单类型说明符中出现:auto(expr) 与 auto{expr} 。从表达式推导它的类型。

(从C++23 开始)此外,auto 与 类型约束 auto (从C++20开始) 还可以在以下地方出现:

  • lambda 表达式的形参声明:[](auto&&){}。这种 lambda 表达式是泛型 lambda。(C++20 起)
  • 函数形参声明:void f(auto);。这种函数声明引入简写函数模板。(C++14 起)
     

类型约束

如果存在类型约束,则设T为占位符推导的类型,类型约束将引入如下约束表达式:

  • 如果类型约束为Concept<A1,…,An>,则约束表达式为Concept<T,A1,……,An>;
  • 否则(类型约束是没有参数列表的Concept),约束表达式是Concept<T>。

如果约束表达式无效或返回false,则推理失败。

注解

C++11 之前,auto 具有存储期说明符的语义。

不允许在一个声明中混合 auto 的变量和函数,如 auto f() -> int, i = 0;。

auto 说明符也可以用于后随尾随返回类型的函数声明符,此时返回类型是它的尾随返回类型(它也可以是占位符类型)。

auto (*p)() -> int; // 声明指向【返回 int 的函数】的指针
auto (*q)() -> auto = p; // 声明 q 为指向【返回 T 的函数】的指针
                         // 其中 T 从 p 的类型推导

还没见过这两种写法

auto 说明符也可以用于结构化绑定声明。

(从C++17开始)auto 关键词也可以用于嵌套名说明符。形如 auto:: 的嵌套名说明符是一个占位符,它会遵循受约束类型占位符的推导规则被替换为某个类或枚举类型。

示例

#include <iostream>
#include <utility>
 

// 返回类型是 operator+(T, U) 的类型
template<class T, class U>
auto add(T t, U u) { return 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>);
 
    auto c0 = a;             // c0 的类型是 int,保有 a 的副本
    decltype(auto) c1 = a;   // c1 的类型是 int,保有 a 的副本
    decltype(auto) c2 = (a); // c2 的类型是 int&,它是 a 的别名
    std::cout << "通过 c2 修改前,a = " << a << '\n';
    ++c2;
    std::cout << "通过 c2 修改后,a = " << 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:DR N3922 起 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++ 错误
 
    [](...){}(c0, c1, v, w, d, n, m, lambda); // 阻止“变量未使用”警告
}

这个代码可能要在c++19环境下跑,我在C++17下没有跑成功

总结

1 auto是根据初始值来确定变量类型的,再进一步,根据初始值的左值/右值之分,适当的添加占位符;

2 如果占位符类型说明符用于声明多个变量,则推导的类型必须匹配;

具体用法,参考给出的这段代码

#include <iostream>
#include <utility>
 
 
// 返回类型是 operator+(T, U) 的类型
// 理解
template<class T, class U>
auto add(T t, U u) { return 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>);
 
    auto c0 = a;             // c0 的类型是 int,保有 a 的副本
    decltype(auto) c1 = a;   // c1 的类型是 int,保有 a 的副本
    decltype(auto) c2 = (a); // c2 的类型是 int&,它是 a 的别名
    std::cout << "通过 c2 修改前,a = " << a << '\n';
    ++c2;
    std::cout << "通过 c2 修改后,a = " << 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:DR N3922 起 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++ 错误
 
    [](...){}(c0, c1, v, w, d, n, m, lambda); // 阻止“变量未使用”警告
}

学习:Placeholder type specifiers (since C++11) - cppreference.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值