C++ | 4-易用性改进

使用auto

自动类型推断,顾名思义,就是编译器能够根据表达式的类型,自动决定变量的类型(从 C++14 开始,还有函数的返回类型),不再需要程序员手工声明。

但需要说明的是,auto 并没有改变 C++ 是静态类型语言这一事实——使用 auto 的变量(或函数返回 值)的类型仍然是编译时就确定了,只不过编译器能自动帮你填充而已。

自动类型推断不仅降低了代码的啰嗦程度,也提高了代码的抽象性,使我 们可以用更少的代码写出通用的功能。

auto 实际使用的规则类似于函数模板参数的推导规则。

     当写了一个含 auto 的表 达式时,相当于把 auto 替换为模板参数的结果。举具体的例子:

        auto a = expr; 意味着用 expr 去匹配一个假想的 template f(T) 函数模板,结果为值类型。

        const auto& a = expr; 意味着用 expr 去匹配一个假想的 template f(const T&) 函数模板,结果为常左值引用类型。

        auto&& a = expr; 意味着用 expr 去匹配一个假想的 template f(T&&) 函数模板,根据 [第 3 讲] 中我们讨论过的转发引用和引用坍缩规则,结果是 一个跟 expr 值类别相同的引用类型。 

 

decltype

decltype 的用途是获得一个表达式的类型,结果可以跟类型一样使用。它有两基本用 法:

  1. decltype(变量名) 可以获得变量的精确类型。

  2. decltype(表达式) (表达式不是变量名,但包括 decltype((变量名)) 的情况)可以获得表达式的引用类型;

      除非表达式的结果是个纯右值(prvalue),此时结果仍然是值类型。

例子:

    如果我们有 int a;,那么:

     decltype(a) 会获得 int(因为 a 是 int)。

     decltype((a)) 会获得 int&(因为 a 是 lvalue,情况二表达式情况)。

     decltype(a + a) 会获得 int(因为 a + a 是 prvalue)。

 decltype(auto)

通常情况下,能写 auto 来声明变量肯定是件比较轻松的事。但这儿有个限制,需要在写 下 auto 时就决定你写下的是个引用类型还是值类型

根据类型推导规则,auto 是值类 型,auto& 是左值引用类型,auto&& 是转发引用(可以是左值引用,也可以是右值引 用)。

       使用 auto 不能通用地根据表达式类型来决定返回值的类型。

不过, decltype(expr) 既可以是值类型,也可以是引用类型。因此,我们可以这么写:

decltype(expr) a = expr;

这种写法明显不能让人满意,特别是表达式很长的情况(而且,任何代码重复都是潜在的问 题)。为此,C++14 引入了 decltype(auto) 语法。对于上面的情况,我们只需要像下 面这样写就行了。

decltype(auto) a = expr;

这种代码主要用在通用的转发函数模板中:可能根本不知道你调用的函数是不是会返回一 个引用。这时使用这种语法就会方便很多。

函数返回值类型推断

从 C++14 开始,函数的返回值也可以用 auto 或 decltype(auto) 来声明了。同样的, 用 auto 可以得到值类型,用 auto& 或 auto&& 可以得到引用类型;

而用 decltype(auto) 可以根据返回表达式通用地决定返回的是值类型还是引用类型。

auto foobar(参数)
{
    //函数体
}
//编译加上-std=c++14

类模板的模板参数推导

结构化绑定

列表初始化

统一初始化

。。。没看懂到总结的程度,待补充。

类数据成员的默认初始化

class Complex {
public:
Complex()
  : re_(0) , im_(0) {}
Complex(float re)
  : re_(re), im_(0) {}
Complex(float re, float im)
  : re_(re) , im_(im) {}
…
private:
  float re_;
float im_;
};

C++11 增加了一个语法,允许 在声明数据成员时直接给予一个初始化表达式。这样,当且仅当构造函数的初始化列表中不 包含该数据成员时,这个数据成员就会自动使用初始化表达式进行初始化。

假设由于某种原因,我们不能使用缺省参数来简化构造函数,可以用什么方式来优化上 面这个代码呢?

class Complex {
public:
  Complex() {}
  Complex(float re) : re_(re) {}
  Complex(float re, float im)
    : re_(re) , im_(im) {}
private:
  float re_{0};
  float im_{0};
}

第一个构造函数没有任何初始化列表,所以类数据成员的初始化全部由默认初始化完成, re_ 和 im_ 都是 0。第二个构造函数提供了 re_ 的初始化,im_ 仍由默认初始化完成。第 三个构造函数则完全不使用默认初始化。

字面量

C++11 引入了自定义字面量,可以使用 operator"" 后缀 来将用户提供的字面量转换成 实际的类型。C++14 则在标准库中加入了不少标准字面量。

用户定义字面量 (C++11 起) - cppreference.com

标准库中定义了下列字面量运算符:

定义于内联命名空间 std::literals::complex_literals
表示纯虚数的 std::complex 字面量
(函数)
定义于内联命名空间 std::literals::chrono_literals
(C++14)
表示小时的 std::chrono::duration 字面量
(函数)
(C++14)
表示分钟的 std::chrono::duration 字面量
(函数)
(C++14)
表示秒的 std::chrono::duration 字面量
(函数)
(C++14)
表示毫秒的 std::chrono::duration 字面量
(函数)
(C++14)
表示微秒的 std::chrono::duration 字面量
(函数)
(C++14)
表示纳秒的 std::chrono::duration 字面量
(函数)
(C++20)
表示特定年的 std::chrono::year 字面量
(函数)
(C++20)
表示月内日期的 std::chrono::day 字面量
(函数)
定义于内联命名空间 std::literals::string_literals
(C++14)
转换字符数组字面量为 basic_string
(函数)
定义于内联命名空间 std::literals::string_view_literals
(C++17)
创建一个字符数组字面量的字符串视图
(函数)

 样例:

#include <chrono>
#include <complex>
#include <iostream>
#include <string>
#include <thread>
using namespace std;
int main()
{
    cout << "i * i = " << 1i * 1i
        << endl;
    cout << "Waiting for 500ms"
        << endl;
    this_thread::sleep_for(500ms);
    cout << "Hello world"s.substr(0, 5)
        << endl;
}

                

输出:

i * i = (-1,0)
Waiting for 500ms
Hello

要在自己的类里支持字面量也相当容易,唯一的限制是非标准的字面量后缀必须以下划线 _ 打头。

自己定义字面量:

struct length {
    double value;
    enum unit 
    {
        metre,
        kilometre,
        millimetre,
        centimetre,
        inch,
        foot,
        yard,
        mile,
    };
    static constexpr double factors[] =
    {   
        1.0, 1000.0, 1e-3,
        1e-2, 0.0254, 0.3048,
        0.9144, 1609.344
    };
    explicit length(double v, unit u = metre)
    {
        value = v * factors[u];
    }
}; 
length operator+(length lhs, length rhs)
{
return length(lhs.value + rhs.value);
}

 可以手写 length(1.0, length::metre) 这样的表达式,但估计大部分开发人员 都不愿意这么做吧。

反过来,如果我们让开发人员这么写,大家应该还是基本乐意的:

1.0_m + 10.0_cm

要允许上面这个表达式,我们只需要提供下面的运算符即可:

length operator"" _m(long double v)
{
    return length(v, length::metre);
}
length operator"" _cm(long double v)
{
    return length(v, length::centimetre);
}

静态断言

C++98 的 assert 允许在运行时检查一个函数的前置条件是否成立。没有一种方法允许开 发人员在编译的时候检查假设是否成立。

C++11 直接从语言层面提供了静态断言机制,不仅能输出更好的 信息,而且适用性也更好,可以直接放在类的定义中,而不像之前用的特殊技巧只能放在函 数体里。

static_assert(编译期条件表达式, 可选输出信息);

for example:

static_assert((alignment & (alignment - 1)) == 0,
"Alignment must be power of two");

default 和 delete 成员函数

override 和 final 说明符

 

原文链接: https://www.cnblogs.com/JohnsonQ/p/17022648.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    C++ | 4-易用性改进

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/309501

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月16日 上午11:06
下一篇 2023年2月16日 上午11:06

相关推荐