spdlog源码学习:std::unique_ptr订制删除器,guard用法,以及decltype

源代码为:

C:\work\hchx\HchxKernel\PublicAPI\spdlog\fmt\bundled\args.h

 template <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();
};

注意,unique_ptr的第一个参数类型是std::vector<basic_format_arg<Context>>,
但初始化的时候,要用其指针std::vector<basic_format_arg<Context>>*
而且自定义删除器的第二个参数,也是std::vector<basic_format_arg<Context>>*类型

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/983223


void 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最合适。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值