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指针会出现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高

std::filesystem

具体可查看cppreference的filesystem

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值