【C++知识点】C++17 常用新特性总结(二)

简介: 【C++知识点】C++17 常用新特性总结(二)

整数转字符串

first, last - 要写入的字符范围。

value - 要转换到其字符串表示的值。

base - 使用的整数基底: 取值范围[2,36]。

std::to_chars_result to_chars(char* first, char* last, float value,std::chars_format fmt, int precision);
std::to_chars_result to_chars(char* first, char* last, double value, std::chars_format fmt, int precision);
std::to_chars_result to_chars(char* first, char* last, long double value, std::chars_format fmt, int precision);

浮点转字符串

fmt - 使用的浮点格式, std::chars_format 类型的位掩码

precision - 使用的浮点精度

案例

#include <iostream>
#include <charconv>
using namespace std;
int main() {
    string s1{ "123456789" };
    int val = 0;
    auto res = from_chars(s1.data(), s1.data() + 4, val);//把s1的前4个转成整数
    if (res.ec == errc()) {
        cout << "val: " << val << ", distance: " << res.ptr - s1.data() << endl;
        // val: 1234, distance: 4
    }
    else if (res.ec == errc::invalid_argument) {
        cout << "invalid" << endl;
    }
    s1 = string{ "12.34" };
    double value = 0;
    auto format = chars_format::general;
    res = from_chars(s1.data(), s1.data() + s1.size(), value, format); //把"12.34"转成小数
    cout << "value: " << value << endl;
    // value: 12.34
    s1 = string{ "xxxxxxxx" };
    int v = 1234;
    auto result = to_chars(s1.data(), s1.data() + s1.size(), v);//把整数转成字符串
    cout << "str: " << s1 << ", filled: " << result.ptr - s1.data()<< " characters." << endl;
    // s1: 1234xxxx, filled: 4 characters.
    return 0; 
}

std::variant

C++17 中提供了 std::variant 类型,意为多变的,可变的类型。

有点类似于加强版的 union,里面可以存放复合数据类型,且操作元素更为方便。

可以用于表示多种类型的混合体,但同一时间只能用于代表一种类型的实例。

variant 提供了 index 成员函数,该函数返回一个索引,该索引用于表示 variant 定义对象时模板参数的索引(起始索引为 0),同时提供了一个函数 holds_alternative(v) 用于查询对象 v 当前存储的值类型是否是 T。

#include <iostream>
#include <variant>
#include <string>
using namespace std;
int main() {
    variant<int, double, string> d; //int 0 double 1 string 2
    cout << d.index() << endl; //输出:0
    d = 3.14;
    cout << d.index() << endl; //输出:1
    d = "hi";
    cout << d.index() << endl; //输出:2
    cout << holds_alternative<int>(d) << endl; //输出:0
    cout << holds_alternative<double>(d) << endl; //输出:0
    cout << holds_alternative<string>(d) << endl; //输出:1
    return 0;
}

std::optional

在 C 时代以及早期 C++ 时代,语法层面支持的 nullable 类型可以采用指针方式:T*,如果指针为 NULL(C++11 之后则使用 nullptr )就表示无值状态(empty value)。

在编程中,经常遇到这样的情况:可能返回/传递/使用某种类型的对象。也就是说,可以有某个类型的值,也可以没有任何值。因此,需要一种方法来模拟类似指针的语义,在指针中,可以使用 nullptr 来表示没有值。


处理这个问题的方法是定义一个特定类型的对象,并用一个额外的布尔成员/标志来表示值是否存在。std::optional<> 以一种类型安全的方式提供了这样的对象。


注意:每个版本可能对某些特征做了改动。

optional 是一个模板类:

template <class T>
class optional;

它内部有两种状态,要么有值(T 类型),要么没有值(std::nullopt)。有点像 T* 指针,要么指向一个 T 类型,要么是空指针(nullptr)。

std::optional 有以下几种构造方式:

#include <iostream>
#include <optional>
using namespace std;
int main() {
    optional<int> o1; //什么都不写时默认初始化为nullopt
    optional<int> o2 = nullopt; //初始化为无值
    optional<int> o3 = 10; //用一个T类型的值来初始化
    optional<int> o4 = o3; //用另一个optional来初始化
    return 0;
}

查看一个 optional 对象是否有值,可以直接用 if,或者用 has_value():

optional<int> o1;
if (o1) {
    printf("o1 has value\n");
}
if (o1.has_value()) {
    printf("o1 has value\n");

当一个 optional 有值时,可以通过用指针的方式(* 号和 -> 号)来使用它,或者用 .value() 拿到它的值:

optional<int> o1 = 100;
cout << *o1 << endl;
optional<string> o2 = "orange";
cout << o2->c_str() << endl;
cout << o2.value().c_str() << endl;

将一个有值的 optional 变为无值,用 .reset()。该函数会将已存储的 T 类型对象析构掉:

optional<int> o1 = 500;
o1.reset();

std::any

在 C++11 中引入的 auto 自动推导类型变量大大方便了编程,但是 auto 变量一旦声明,该变量类型不可再改变。

C++17 中引入了 std::any 类型,该类型变量可以存储任何类型的值,也可以时刻改变它的类型,类似于 python 中的变量。

#include <any>
#include <iostream>
using namespace std;
int main() {
    cout << boolalpha; //将bool值用"true"和"false"显示
    any a; //定义一个空的any,即一个空的容器
    //有两种方法来判断一个any是否是空的
    cout << a.has_value() << endl; //any是空的时,has_value返回值为false
    cout << a.type().name() << endl; //any是空的时,has_value返回值为true
    //几种创建any的方式
    any b = 1; //b为存了int类型的值的any
    auto c = make_any<float>(5.0f); //c为存了float类型的any
    any d(6.0); //d为存储了double类型的any
    cout << b.has_value() << endl; //true 
    cout << b.type().name() << endl; //int
    cout << c.has_value() << endl; //true
    cout << c.type().name() << endl; //float
    cout << d.has_value() << endl; //true
    cout << d.type().name() << endl; //double
    //更改any的值 
    a = 2; //直接重新赋值
    auto e = c.emplace<float>(4.0f); //调用emplace函数,e为新生成的对象引用
    //清空any的值
    b.reset();
    cout << b.has_value() << endl; //false
    cout << b.type().name() << endl; //int
    //使用any的值
    try
    {
        auto f = any_cast<int>(a); //f为int类型,其值为2
        cout << f << endl; //2
    }
    catch (const bad_any_cast& e)
    {
        cout << e.what() << endl;
    }
    try
    {
        auto g = any_cast<float>(a); //抛出std::bad_any_cat 异常
        cout << g << endl; //该语句不会执行
    }
    catch (const bad_any_cast& e)
    {
        cout << e.what() << endl; //可能输出Bad any_cast
    }
    return 0;
}

std::apply

将 tuple 元组解包,并作为函数的传入参数。

#include <any>
#include <iostream>
using namespace std;
int add(int a, int b) { 
    return a + b; 
}
int main() {
    auto add_lambda = [](auto a, auto b, auto c) { return a + b + c; };
    cout << apply(add, pair(2, 3)) << endl; //5
    cout << apply(add_lambda, tuple(2, 3, 4)) << endl; //9
    return 0;
}

std::make_from_tuple

解包 tuple 作为构造函数参数构造对象。

#include <iostream>
using namespace std;
class ClassTest {
    public:
    string _name;
    size_t _age;
    ClassTest(string name, size_t age) : _name(name), _age(age) { 
        cout << "name: " << _name << ", age: " << _age << endl;
    }
};
int main() {
    auto param = make_tuple("zhangsan", 19);
    make_from_tuple<ClassTest>(move(param));
    return 0;
}

std::string_view

C++ 中字符串有两种形式,char* 和 std::string,string 类型封装了 char* 字符串,让我们对字符串的操作方便了很多,但是会有些许性能的损失,而且由 char* 转为 string 类型,需要调用 string 类拷贝构造函数,也就是说需要重新申请一片内存,但如果只是对源字符串做只读操作,这样的构造行为显然是不必要的。


在 C++17 中,增加了 std::string_view 类型,它通过 char* 字符串构造,但是并不会去申请内存重新创建一份该字符串对象,只是 char*` 字符串的一个视图,优化了不必要的内存操作。相应地,对源字符串只有读权限,没有写权限。

#include <iostream>
using namespace std;
void func1(string_view str_v) {
    cout << str_v << endl;
    return;
}
int main() {
    const char* charStr = "hello world";
    string str{ charStr };
    string_view str_v(charStr, strlen(charStr));
    cout << "str: " << str << endl;
    cout << "str_v: " << str_v << endl;
    func1(str_v);
    return 0;
}

std::as_const

将左值转化为 const 类型。

#include <iostream>
using namespace std;
int main() {
    string str={ "C++ as const test" };
    cout << is_const<decltype(str)>::value << endl;
    const string str_const = as_const(str);
    cout << is_const<decltype(str_const)>::value << endl;
    return 0;
}

std::filesystem

C++17 中引入了 filesystem,方便处理文件,提供接口函数很多,用起来也很方便。

一定要是 C++17 标准及以上版本。

项目属性->C/C+±>语言->C++语言标准设置为:ISO C++17 标准(/std:c++17)

头文件及命名空间

#include<filesystem>
using namespace std::filesystem

常用类

  • path 类:路径处理
  • directory_entry 类:文件入口
  • directory_iterator 类:获取文件系统目录中文件的迭代器容器
  • file_status 类:用于获取和修改文件(或目录)的属性

常用函数

  • std::filesystem::exists(const path& pval):用于判断 path 是否存在
  • std::filesystem::copy(const path& from, const path& to):目录复制
  • std::filesystem::absolute(const path& pval, const path& base = current_path()):=:获取相对于 base 的绝对路径
  • std::filesystem::create_directory(const path& pval):当目录不存在时创建目录
  • std::filesystem::create_directories(const path& pval):形如 /a/b/c 这样的,如果都不存在,创建目录结构
  • std::filesystem::file_size(const path& pval):返回目录的大小
#include <ctime>
#include <iostream>
#include <filesystem>
using namespace std;
int main() {
    namespace fs = std::filesystem;
    auto testdir = fs::path("./testdir");
    if (!fs::exists(testdir))//文件是否存在
    {
        cout << "file or directory is not exists!" << endl;
    }
    //none(默认)跳过符号链接,权限拒绝是错误。
    //follow_directory_symlink 跟随而非跳过符号链接。
    //skip_permission_denied 跳过若不跳过就会产生权限拒绝错误的目录。
    fs::directory_options opt(fs::directory_options::none);
    fs::directory_entry dir(testdir);
    //遍历当前目录
    std::cout << "show:\t" << dir.path().filename() << endl;
    for (fs::directory_entry const& entry : fs::directory_iterator(testdir, opt))
    {
        if (entry.is_regular_file())
        {
            cout << entry.path().filename() << "\t size: " << entry.file_size() << endl;
        }
        else if (entry.is_directory())
        {
            cout << entry.path().filename() << "\t dir" << endl;
        }
    }
    cout << endl;
    //递归遍历所有的文件
    cout << "show all:\t" << dir.path().filename() << endl;
    for (fs::directory_entry const& entry : fs::recursive_directory_iterator(testdir, opt))
    {
        if (entry.is_regular_file())
        {
            cout << entry.path().filename() << "\t size: " << entry.file_size() << "\t parent: " << entry.path().parent_path() << endl;
        }
        else if (entry.is_directory())
        {
            cout << entry.path().filename()<< "\t dir" << endl;
        }
    }
    return 0;
}
目录
相关文章
|
3月前
|
编译器 C++ 开发者
C++一分钟之-C++20新特性:模块化编程
【6月更文挑战第27天】C++20引入模块化编程,缓解`#include`带来的编译时间长和头文件管理难题。模块由接口(`.cppm`)和实现(`.cpp`)组成,使用`import`导入。常见问题包括兼容性、设计不当、暴露私有细节和编译器支持。避免这些问题需分阶段迁移、合理设计、明确接口和关注编译器更新。示例展示了模块定义和使用,提升代码组织和维护性。随着编译器支持加强,模块化将成为C++标准的关键特性。
142 3
|
3月前
|
编译器 C语言 C++
C++一分钟之-C++11新特性:初始化列表
【6月更文挑战第21天】C++11的初始化列表增强语言表现力,简化对象构造,特别是在处理容器和数组时。它允许直接初始化成员变量,提升代码清晰度和性能。使用时要注意无默认构造函数可能导致编译错误,成员初始化顺序应与声明顺序一致,且在重载构造函数时避免歧义。利用编译器警告能帮助避免陷阱。初始化列表是高效编程的关键,但需谨慎使用。
48 2
|
1月前
|
安全 NoSQL Redis
C++新特性-智能指针
C++新特性-智能指针
|
3月前
|
存储 网络协议 编译器
【干货总结】Linux C/C++面试知识点
Linux C/C++基础与进阶知识点,不仅用于面试,平时开发也用得上!
519 13
|
2月前
|
数据安全/隐私保护 C++
|
3月前
|
安全 JavaScript 前端开发
C++一分钟之-C++17特性:结构化绑定
【6月更文挑战第26天】C++17引入了结构化绑定,简化了从聚合类型如`std::tuple`、`std::array`和自定义结构体中解构数据。它允许直接将复合数据类型的元素绑定到单独变量,提高代码可读性。例如,可以从`std::tuple`中直接解构并绑定到变量,无需`std::get`。结构化绑定适用于处理`std::tuple`、`std::pair`,自定义结构体,甚至在范围for循环中解构容器元素。注意,绑定顺序必须与元素顺序匹配,考虑是否使用`const`和`&`,以及谨慎处理匿名类型。通过实例展示了如何解构嵌套结构体和元组,结构化绑定提升了代码的简洁性和效率。
60 5
|
2月前
|
存储 安全 编译器
|
3月前
|
C++
C++ 是一种面向对象的编程语言,它支持对象、类、继承、多态等面向对象的特性
C++ 是一种面向对象的编程语言,它支持对象、类、继承、多态等面向对象的特性
|
1天前
|
编译器 C++
C++ 类构造函数初始化列表
构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式。
41 30
|
16天前
|
存储 编译器 C++
C ++初阶:类和对象(中)
C ++初阶:类和对象(中)