spdlog源码学习:std::unique_ptr订制删除器,guard用法,以及decltype
源代码为:
C:\work\hchx\HchxKernel\PublicAPI\spdlog\fmt\bundled\args.htemplate <typename T>void emplace_arg(const detail::named_arg<char_type, T>& arg) {if (named_info_.empty()) {constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr};data_.insert(data_.begin(), {zero_ptr, 0});}data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value)));auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {data->pop_back();};std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>guard{&data_, pop_one};named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});data_[0].value_.named_args = {named_info_.data(), named_info_.size()};guard.release();}
取出其中的一部分,观察guard的概念:
1、std::unique_ptr肯定会析构,除非提前release出来,交出管理权
//unique_ptr的自定义删除器
auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {data->pop_back();
};std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>guard{&data_, pop_one};named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});data_[0].value_.named_args = {named_info_.data(), named_info_.size()};//如果上面两行代码又异常提前退出。代码就会跳到pop_one里,进行回退,也就是pop_back
guard.release();
下面研究下unique_ptr自定义删除器的用法
note:这种写法,在C++14下编译不过:lambda 不能出现在未计算的上下文中
必须更高的版本。
自定义的实现不能写在std::unique_ptr的尖括号的里面auto main()->int{// 未求值的lambda,g++ main.cpp -std=c++20// 适合单次使用,不适合做接口,因为lambda表达式在每次使用都属于不同的类型std::unique_ptr<FILE, decltype([](FILE* fp){fclose(fp); fp = nullptr;
std::cout << "file auto closed.\n";})>
pfile(fopen("3.txt", "w"));
}
}
————————————————版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。原文链接:https://blog.csdn.net/liugan528/article/details/139031545
得写成这样
注意,初始化pfile时的第二个参数。要把del传给它。auto fun3()->int {auto del = [](FILE* fp)->void {fclose(fp);fp = nullptr; std::cout << "file auto closed.\n"; };// 未求值的lambda,g++ main.cpp -std=c++20// 适合单次使用,不适合做接口,因为lambda表达式在每次使用都属于不同的类型std::unique_ptr < FILE, decltype(del) >pfile(fopen("3.txt", "w"), del);return 1;
}
再来一个
void fun4()
{auto del = [](FILE* fp)->void {fclose(fp);fp = nullptr; std::cout << "file auto closed.\n"; };auto delLambda = [](int* p) { delete p; };std::unique_ptr<int, decltype(delLambda)> p3(nullptr, delLambda);
}
注意上面例子中decltype的用法,auto的类型用decltype进行推导。
看看别人的例子,展示了std::function用于删除器时,字节消耗的最多
https://developer.aliyun.com/article/983223void Delete(int* p) { delete p; }
auto delLambda = [](int* p) { delete p; };
std::function<void (int*)> delFunc = delLambda;int main()
{std::unique_ptr<int> p1(nullptr);std::unique_ptr<int, decltype(Delete)*> p2(nullptr, Delete);std::unique_ptr<int, decltype(delLambda)> p3(nullptr, delLambda);std::unique_ptr<int, decltype(delFunc)> p4(nullptr, delFunc);std::cout << "Default deleter: " << sizeof(p1) << std::endl;std::cout << "FuncPtr deleter: " << sizeof(p2) << std::endl;std::cout << "Lambda deleter: " << sizeof(p3) << std::endl;std::cout << "Function deleter: " << sizeof(p4) << std::endl;return 0;
}// Default deleter: 8
// FuncPtr deleter: 16
// Lambda deleter: 8
// Function deleter: 40可以看到:
使用函数指针作为删除器产生的std::unique_ptr 对象大小为 16 字节,
使用 std::function 则为 40 字节,
而使用无状态 lambda 和默认删除器均为 8 字节,
果真没有引入额外的内存开销。看来还是lambda最合适。