c++11特性与c++17特性
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指针会出现SIGSEGVreturn 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类型, 会调用一次拷贝构造, 当不存在拷贝构造时无法捕获*thisassert(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_startprocpushq %rbpmovq %rsp, %rbpsubq $16, %rspmovl %edi, -4(%rbp)movq %rsi, -16(%rbp)call _Z14getDefaultPathILb1EEPKcvmovl $0, %eaxleave.cfi_def_cfa 7, 8ret.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_startprocpushq %rbpmovq %rsp, %rbpleaq .LC0(%rip), %raxmovq %rax, -8(%rbp)movq -8(%rbp), %raxpopq %rbpret.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替换0std::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高
std::filesystem
具体可查看cppreference的filesystem