程序喵之前曾经介绍过C++11的新个性和C++14的新个性,链接如下:xxx,明天向敬爱的读者们介绍下C++17的新个性,当初基本上各个编译器对C++17都曾经提供齐备的反对,倡议大家编程中尝试应用下C++17,能够肯定水平上简化代码编写,进步编程效率。

次要新个性如下:

  • 构造函数模板推导
  • 结构化绑定
  • if-switch语句初始化
  • 内联变量
  • 折叠表达式
  • constexpr lambda表达式
  • namespace嵌套
  • __has_include预处理表达式
  • 在lambda表达式用*this捕捉对象正本
  • 新增Attribute
  • 字符串转换
  • std::variant
  • std::optional
  • std::any
  • std::apply
  • std::make_from_tuple
  • as_const
  • std::string_view
  • file_system
  • std::shared_mutex

上面程序喵一一介绍:

构造函数模板推导

在C++17前结构一个模板类对象须要指明类型:

pair<int, double> p(1, 2.2); // before c++17

C++17就不须要非凡指定,间接能够推导出类型,代码如下:

pair p(1, 2.2); // c++17 主动推导vector v = {1, 2, 3}; // c++17

结构化绑定

通过结构化绑定,对于tuple、map等类型,获取相应值会不便很多,看代码:

std::tuple<int, double> func() {    return std::tuple(1, 2.2);}int main() {    auto[i, d] = func(); //是C++11的tie吗?更高级    cout << i << endl;    cout << d << endl;}//==========================void f() {    map<int, string> m = {      {0, "a"},      {1, "b"},      };    for (const auto &[i, s] : m) {        cout << i << " " << s << endl;    }}// ====================int main() {    std::pair a(1, 2.3f);    auto[i, f] = a;    cout << i << endl; // 1    cout << f << endl; // 2.3f    return 0;}

结构化绑定还能够扭转对象的值,应用援用即可:

// 进化,能够通过结构化绑定扭转对象的值int main() {    std::pair a(1, 2.3f);    auto& [i, f] = a;    i = 2;    cout << a.first << endl; // 2 }

留神结构化绑定不能利用于constexpr

constexpr auto[x, y] = std::pair(1, 2.3f); // compile error, C++20能够

结构化绑定不止能够绑定pair和tuple,还能够绑定数组和构造体等

int array[3] = {1, 2, 3};auto [a, b, c] = array;cout << a << " " << b << " " << c << endl;// 留神这里的struct的成员肯定要是public的struct Point {    int x;    int y;};Point func() {    return {1, 2};}const auto [x, y] = func();

这里其实能够实现自定义类的结构化绑定,代码如下:

// 须要实现相干的tuple_size和tuple_element和get<N>办法。class Entry {public:    void Init() {        name_ = "name";        age_ = 10;    }    std::string GetName() const { return name_; }    int GetAge() const { return age_; }private:    std::string name_;    int age_;};template <size_t I>auto get(const Entry& e) {    if constexpr (I == 0) return e.GetName();    else if constexpr (I == 1) return e.GetAge();}namespace std {    template<> struct tuple_size<Entry> : integral_constant<size_t, 2> {};    template<> struct tuple_element<0, Entry> { using type = std::string; };    template<> struct tuple_element<1, Entry> { using type = int; };}int main() {    Entry e;    e.Init();    auto [name, age] = e;    cout << name << " " << age << endl; // name 10    return 0;}

if-switch语句初始化

C++17前if语句须要这样写代码:

int a = GetValue();if (a < 101) {    cout << a;}

C++17之后能够这样:

// if (init; condition)if (int a = GetValue()); a < 101) {    cout << a;}string str = "Hi World";if (auto [pos, size] = pair(str.find("Hi"), str.size()); pos != string::npos) {    std::cout << pos << " Hello, size is " << size;}

应用这种形式能够尽可能束缚作用域,让代码更简洁,可读性可能略有降落,然而还好

内联变量

C++17前只有内联函数,当初有了内联变量,咱们印象中C++类的动态成员变量在头文件中是不能初始化的,然而有了内联变量,就能够达到此目标:

// header filestruct A {    static const int value;  };inline int const A::value = 10;// ==========或者========struct A {    inline static const int value = 10;}

折叠表达式

C++17引入了折叠表达式使可变参数模板编程更不便:

template <typename ... Ts>auto sum(Ts ... ts) {    return (ts + ...);}int a {sum(1, 2, 3, 4, 5)}; // 15std::string a{"hello "};std::string b{"world"};cout << sum(a, b) << endl; // hello world

constexpr lambda表达式

C++17前lambda表达式只能在运行时应用,C++17引入了constexpr lambda表达式,能够用于在编译期进行计算。

int main() { // c++17可编译    constexpr auto lamb = [] (int n) { return n * n; };    static_assert(lamb(3) == 9, "a");}

留神:constexpr函数有如下限度:

函数体不能蕴含汇编语句、goto语句、label、try块、动态变量、线程部分存储、没有初始化的一般变量,不能动静分配内存,不能有new delete等,不能虚函数。

namespace嵌套

namespace A {    namespace B {        namespace C {            void func();        }    }}// c++17,更不便更舒服namespace A::B::C {    void func();)}

__has_include预处理表达式

能够判断是否有某个头文件,代码可能会在不同编译器下工作,不同编译器的可用头文件有可能不同,所以能够应用此来判断:

#if defined __has_include#if __has_include(<charconv>)#define has_charconv 1#include <charconv>#endif#endifstd::optional<int> ConvertToInt(const std::string& str) {    int value{};#ifdef has_charconv    const auto last = str.data() + str.size();    const auto res = std::from_chars(str.data(), last, value);    if (res.ec == std::errc{} && res.ptr == last) return value;#else    // alternative implementation...    其它形式实现#endif    return std::nullopt;}

在lambda表达式用*this捕捉对象正本

失常状况下,lambda表达式中拜访类的对象成员变量须要捕捉this,然而这里捕捉的是this指针,指向的是对象的援用,失常状况下可能没问题,然而如果多线程状况下,函数的作用域超过了对象的作用域,对象曾经被析构了,还拜访了成员变量,就会有问题。

struct A {    int a;    void func() {        auto f = [this] {            cout << a << endl;        };        f();    }  };int main() {    A a;    a.func();    return 0;}

所以C++17减少了新个性,捕捉*this,不持有this指针,而是持有对象的拷贝,这样生命周期就与对象的生命周期不相干啦。

struct A {    int a;    void func() {        auto f = [*this] { // 这里            cout << a << endl;        };        f();    }  };int main() {    A a;    a.func();    return 0;}

新增Attribute

咱们可能平时在我的项目中见过__declspec, attribute , #pragma批示符,应用它们来给编译器提供一些额定的信息,来产生一些优化或特定的代码,也能够给其它开发者一些提示信息。

例如:

struct A { short f[3]; } __attribute__((aligned(8)));void fatal() __attribute__((noreturn));

在C++11和C++14中有更不便的办法:

[[carries_dependency]] 让编译期跳过不必要的内存栅栏指令[[noreturn]] 函数不会返回[[deprecated]] 函数将弃用的正告[[noreturn]] void terminate() noexcept;[[deprecated("use new func instead")]] void func() {}

C++17又新增了三个:

[[fallthrough]],用在switch中提醒能够间接落下去,不须要break,让编译期疏忽正告

switch (i) {}    case 1:        xxx; // warning    case 2:        xxx;         [[fallthrough]];      // 正告打消    case 3:        xxx;       break;}

使得编译器和其它开发者都能够了解开发者的用意。

[[nodiscard]] :示意润饰的内容不能被疏忽,可用于润饰函数,表明返回值肯定要被解决

[[nodiscard]] int func();void F() {    func(); // warning 没有处理函数返回值}

[[maybe_unused]] :提醒编译器润饰的内容可能临时没有应用,防止产生正告

void func1() {}[[maybe_unused]] void func2() {} // 正告打消void func3() {    int x = 1;    [[maybe_unused]] int y = 2; // 正告打消}

字符串转换

新增from_chars函数和to_chars函数,间接看代码:

#include <charconv>int main() {    const std::string str{"123456098"};    int value = 0;    const auto res = std::from_chars(str.data(), str.data() + 4, value);    if (res.ec == std::errc()) {        cout << value << ", distance " << res.ptr - str.data() << endl;    } else if (res.ec == std::errc::invalid_argument) {        cout << "invalid" << endl;    }    str = std::string("12.34);    double val = 0;    const auto format = std::chars_format::general;    res = std::from_chars(str.data(), str.data() + str.size(), value, format);        str = std::string("xxxxxxxx");    const int v = 1234;    res = std::to_chars(str.data(), str.data() + str.size(), v);    cout << str << ", filled " << res.ptr - str.data() << " characters \n";    // 1234xxxx, filled 4 characters}

std::variant

C++17减少std::variant实现相似union的性能,但却比union更高级,举个例子union外面不能有string这种类型,但std::variant却能够,还能够反对更多简单类型,如map等,看代码:

int main() { // c++17可编译    std::variant<int, std::string> var("hello");    cout << var.index() << endl;    var = 123;    cout << var.index() << endl;    try {        var = "world";        std::string str = std::get<std::string>(var); // 通过类型获取值        var = 3;        int i = std::get<0>(var); // 通过index获取对应值        cout << str << endl;        cout << i << endl;    } catch(...) {        // xxx;    }    return 0;}

留神:个别状况下variant的第一个类型个别要有对应的构造函数,否则编译失败:

struct A {    A(int i){}  };int main() {    std::variant<A, int> var; // 编译失败}

如何防止这种状况呢,能够应用std::monostate来打个桩,模仿一个空状态。

std::variant<std::monostate, A> var; // 能够编译胜利

std::optional

咱们有时候可能会有需要,让函数返回一个对象,如下:

struct A {};A func() {    if (flag) return A();    else {        // 异常情况下,怎么返回异样值呢,想返回个空呢    }}

有一种方法是返回对象指针,异常情况下就能够返回nullptr啦,然而这就波及到了内存治理,兴许你会应用智能指针,但这里其实有更不便的方法就是std::optional。

std::optional<int> StoI(const std::string &s) {    try {        return std::stoi(s);    } catch(...) {        return std::nullopt;    }}void func() {    std::string s{"123"};    std::optional<int> o = StoI(s);    if (o) {        cout << *o << endl;    } else {        cout << "error" << endl;    }}

std::any

C++17引入了any能够存储任何类型的单个值,见代码:

int main() { // c++17可编译    std::any a = 1;    cout << a.type().name() << " " << std::any_cast<int>(a) << endl;    a = 2.2f;    cout << a.type().name() << " " << std::any_cast<float>(a) << endl;    if (a.has_value()) {        cout << a.type().name();    }    a.reset();    if (a.has_value()) {        cout << a.type().name();    }    a = std::string("a");    cout << a.type().name() << " " << std::any_cast<std::string>(a) << endl;    return 0;}

std::apply

应用std::apply能够将tuple开展作为函数的参数传入,见代码:

int add(int first, int second) { return first + second; }auto add_lambda = [](auto first, auto second) { return first + second; };int main() {    std::cout << std::apply(add, std::pair(1, 2)) << '\n';    std::cout << add(std::pair(1, 2)) << "\n"; // error    std::cout << std::apply(add_lambda, std::tuple(2.0f, 3.0f)) << '\n';}

std::make_from_tuple

应用make_from_tuple能够将tuple开展作为结构函数参数

struct Foo {    Foo(int first, float second, int third) {        std::cout << first << ", " << second << ", " << third << "\n";    }};int main() {   auto tuple = std::make_tuple(42, 3.14f, 0);   std::make_from_tuple<Foo>(std::move(tuple));}

std::string_view

通常咱们传递一个string时会触发对象的拷贝操作,大字符串的拷贝赋值操作会触发堆内存调配,很影响运行效率,有了string_view就能够防止拷贝操作,平时传递过程中传递string_view即可。

void func(std::string_view stv) { cout << stv << endl; }int main(void) {    std::string str = "Hello World";    std::cout << str << std::endl;    std::string_view stv(str.c_str(), str.size());    cout << stv << endl;    func(stv);    return 0;}

as_const

C++17应用as_const能够将左值转成const类型

std::string str = "str";const std::string& constStr = std::as_const(str);

file_system

C++17正式将file_system纳入规范中,提供了对于文件的大多数性能,基本上包罗万象,这里简略举几个例子:

namespace fs = std::filesystem;fs::create_directory(dir_path);fs::copy_file(src, dst, fs::copy_options::skip_existing);fs::exists(filename);fs::current_path(err_code);

std::shared_mutex

C++17引入了shared_mutex,能够实现读写锁,具体能够见我上一篇文章:

对于C++17的介绍就到这里,心愿对大家有所帮忙~

参考资料

https://en.cppreference.com/w...

https://en.cppreference.com/w...

https://en.cppreference.com/w...

https://cloud.tencent.com/dev...

https://www.jianshu.com/p/9b8...
更多文章,请关注我的V X 公 主 号:程序喵小孩儿,欢送交换。