1、自动类型推导auto
// C++11
auto func1() -> int // 需要指定返回值类型
{
return 10;
}
auto func2() -> std::function<void()>
{
auto lambda = []() { };
return lambda;
}
// c++17
// 之后无需指定返回值类型
auto func1()
{
return 10;
}
auto func2()
{
auto lambda = []() { };
return lambda;
}
lambda
// C++11
class Any {
public:
Any() { printf("%s()\n", __func__); }
~Any() { printf("%s()\n", __func__); }
void print() const { printf("%s()\n", __func__); }
auto exec() -> std::function<void()>
{
auto lambda = [this]()
{
this->print();
};
return lambda;
}
};
auto get() -> std::function<void()>
{
Any any;
return any.exec();
}
int main(int argc, char **argv)
{
get()(); // 调用一个已经析构对象的this指针会出现SIGSEGV
return 0;
}
// C++17
class Any {
public:
Any() : this_e(this) { printf("%s() this = %p\n", __func__, this); }
Any(const Any &other) { printf("%s(const Any &other)\n", __func__); }
~Any() { printf("%s()\n", __func__); }
void print() const { printf("%s() this = %p\n", __func__, this); }
void print_nonconst() { printf("%s()\n", __func__); }
auto exec() -> std::function<void()>
{
auto lambda = [*this]()
{
// 此时*this是const Any类型, 会调用一次拷贝构造, 当不存在拷贝构造时无法捕获*this
assert(this != this_e); // 由于拷贝构造不会修改this_e的值,故this_e还保留着之前对象的地址
this->print(); // 同样可以使用this(但只能调用const属性的函数), 此时的this是*this新创建的Any的地址
// 通过const_cast来消除const,从而调用非const属性的函数
const_cast<Any *>(this)->print_nonconst();
};
return lambda;
}
void *this_e;
};
/**
* c++11无法捕获*this,导致在get()函数返回的lambda在main函数调用时会崩溃
*
* c++17后可以捕获*this,以获取对象的拷贝
*/
auto get() -> std::function<void()>
{
Any any;
return any.exec();
}
int main(int argc, char **argv)
{
get()();
return 0;
}
结构化绑定
// C++17
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <string>
#include <map>
struct Node
{
int size;
char buffer[128];
};
Node &get()
{
static Node node;
node.size = 10;
strncpy(node.buffer, "Hello World", sizeof(node.buffer));
printf("node.buffer = %p\n", node.buffer);
return node;
}
void func()
{
std::map<std::string, int> map;
map["Hello"] = 100;
map["World"] = 200;
// 在c++17之前需要声明iterator, 通过iterator访问
for (const auto &[key, val] : map) {
std::cout << "key = " << key << ", value = " << val << std::endl;
}
}
int main(int argc, char **argv)
{
// 此处声明时需与对象的元素个数一致,且按照元素顺序声明
// 可以是auto(拷贝), auto &(引用), const auto &(常量引用)
const auto &[size, buffer] = get();
std::cout << "size = " << size << ", buffer = " << buffer << std::endl;
printf("main() buffer = %p\n", buffer);
func();
return 0;
}
聚合体
#include <iostream>
#include <string>
struct Base {
Base(const std::string &_s, int _l) : str(_s), len(_l) { }
std::string str;
int len;
};
struct Other : public Base {
bool valid;
};
int main(int argc, char **argv)
{
{
// 构造函数
Base b("hello", 5);
}
{
// 聚合体初始化
Base b = {"hello", 5};
Base b_{"hello", 5}; // C++11之后可以省略等号
// 一般情况下不建议使用如下方式初始化,当成员增多时这种写就会生涩难懂且繁琐
Other o{{"hello", 5}, true}; // C++17之后支持初始化基类(gcc和msvc1920不支持省略基类的大括号)
// 但聚合体初始化常可以用来初始化一些带有默认值的静态对象
// QT的源代码中存在许多聚合体初始化
// 比如 QHashData::shared_null
}
return 0;
}
/**
* 聚合体初始化需要满足
* 1、子类和父类成员需得是public
* 2、子类父类都没有虚函数
*/
新增属性
[[fallthrough]]
[[nodiscard]]
[[maybe_unused]]
if constexpr
编译期如果满足条件,则只对if的内容进行编译,省去else的编译,反之只编译else内容
template <bool flag>
const char *getDefaultPath()
{
const char * path;
if constexpr(flag) {
path = "/default/ptah";
} else {
path = "/other";
}
return path;
}
int main(int argc, char **argv)
{
getDefaultPath<true>();
return 0;
}
// g++ -S test.cc -o test.s -std=c++17
.file "test.cc"
.text
.globl main
.type main, @function
main:
.LFB1:
.cfi_startproc
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
movl %edi, -4(%rbp)
movq %rsi, -16(%rbp)
call _Z14getDefaultPathILb1EEPKcv
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size main, .-main
.section .rodata
.LC0:
.string "/default/ptah"
.section .text._Z14getDefaultPathILb1EEPKcv,"axG",@progbits,_Z14getDefaultPathILb1EEPKcv,comdat
.weak _Z14getDefaultPathILb1EEPKcv
.type _Z14getDefaultPathILb1EEPKcv, @function
_Z14getDefaultPathILb1EEPKcv:
.LFB2:
.cfi_startproc
pushq %rbp
movq %rsp, %rbp
leaq .LC0(%rip), %rax
movq %rax, -8(%rbp)
movq -8(%rbp), %rax
popq %rbp
ret
.cfi_endproc
.LFE2:
.size _Z14getDefaultPathILb1EEPKcv, .-_Z14getDefaultPathILb1EEPKcv
.ident "GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0"
.section .note.GNU-stack,"",@progbits
组件库
std::optional<>
class Any {
public:
Any() { printf("%s()\n", __func__); }
Any(const Any &other) = delete; /// { printf("%s(const Any &other)\n", __func__); }
~Any() { printf("%s()\n", __func__); }
};
int main(int argc, char **argv)
{
// std::optional支持等号运算符,使用等号时会产生一次拷贝
// std::optional<Any> opt = Any();
// 通过std::make_optional构造的不会产生拷贝
std::optional<Any> opt = std::make_optional<Any>();
if (opt) { // or opt.has_value()
std::cout << "opt.value()" << std::endl;
}
return 0;
}
std::variant<>
作用类似union,但是比union更加方便。std::variant只包含模板参数中的一个类型的值,当转换为其他值时会抛出异常。
std::variant是一个变参模板类,可以存放更多的类类型,而不仅限于基础类型。
#include <stdio.h>
#include <iostream>
#include <variant>
#include <assert.h>
class Any {
public:
Any() { printf("%s()\n", __func__); }
Any(const Any &other) { printf("%s(const Any &other)\n", __func__); }
~Any() { printf("%s()\n", __func__); }
};
int main(int argc, char **argv)
{
std::variant<std::string, Any> var_union("hello");
assert(0 == var_union.index());
const std::string &val = std::get<0>(var_union); // 编译期代码,index()属于运行期,所以不能用index替换0
std::cout << val << std::endl;
try {
const Any &any = std::get<Any>(var_union);
assert(true && "never reach here");
} catch (...) {
std::cout << "catch exception\n";
}
var_union = Any();
assert(1 == var_union.index());
return 0;
}
std::any
std::any可以储存任何值的类型,下面以360开源的代码为例
#include <typeinfo>
#include <utility>
#include <assert.h>
// 其核心是Placeholder, 并且运用到了RTTI技术来做安全的类型转换
namespace eular {
// NOTE: class Any need the copy structure
class Any {
public:
Any() : content_(nullptr) {}
~Any() {
if (content_) {
delete content_;
}
}
template<typename ValueType>
explicit Any(const ValueType& value)
: content_(new Holder<ValueType>(value)) {}
Any(const Any& rhs)
: content_(rhs.content_ ? rhs.content_->clone() : nullptr) {}
public:
Any& swap(Any& rhs) {
std::swap(content_, rhs.content_);
return *this;
}
template<typename ValueType>
Any& operator=(const ValueType& rhs) {
Any(rhs).swap(*this);
return *this;
}
Any& operator=(const Any& rhs) {
Any(rhs).swap(*this);
return *this;
}
bool empty() const {
return !content_;
}
const std::type_info& type() const {
return content_ ? content_->getType() : typeid(void);
}
template<typename ValueType>
ValueType operator()() const {
if (getType() == typeid(ValueType)) {
return static_cast<Any::Holder<ValueType>*>(content_)->held_;
} else {
return ValueType();
}
}
protected:
class PlaceHolder {
public:
virtual ~PlaceHolder() {}
public:
virtual const std::type_info& getType() const = 0;
virtual PlaceHolder* clone() const = 0;
};
template<typename ValueType>
class Holder : public PlaceHolder {
public:
Holder(const ValueType& value)
: held_(value) {}
virtual const std::type_info& getType() const {
return typeid(ValueType);
}
virtual PlaceHolder* clone() const {
return new Holder(held_);
}
ValueType held_;
};
protected:
PlaceHolder* content_;
template<typename ValueType>
friend ValueType* any_cast(Any*);
};
template<typename ValueType>
ValueType* any_cast(Any* any) {
if (any && any->getType() == typeid(ValueType)) {
return &(static_cast<Any::Holder<ValueType>*>(any->content_)->held_);
}
return nullptr;
}
template<typename ValueType>
const ValueType *any_cast(const Any* any) {
return any_cast<ValueType>(const_cast<Any *>(any));
}
template<typename ValueType>
ValueType any_cast(const Any& any) {
const ValueType *result = any_cast<ValueType>(&any);
assert(result);
if (!result) {
return ValueType();
}
return *result;
}
std::shared_mutex
读写锁
std::string_view
std::string_view对指向的内容是只读的,其操作是对指针的偏移,所以性能比std::string高