spdlog是一个c++实现的日志库,代码中大量使用了c++11的特性,并且只需要头文件就可以使用,十分值得使用和研究。
下面这段代码是spdlog中的一个工厂函数,用来创建一个新的logger,里面用到了c++11之后才支持的完美转发。
|-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8
| template<typename Sink, typename... SinkArgs> static std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&... args) { auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...); auto new_logger = std::make_shared<spdlog::logger>(std::move(logger_name), std::move(sink)); details::registry::instance().initialize_logger(new_logger); return new_logger; }
|
SinkArgs这个可变长度的模板参数是完美转发的目标,定义成&&,右值引用。std::make_shared会在构造Sink对象时将SinkArgs传递给Sink的构造函数,SinkArgs需要使用std::forward传递,这样就能将参数原原本本的传递给Sink的构造函数,当Sink有不同的构造函数时还能自动匹配。
我们来写代码验证一下。
首先写一个有多种构造函数的类Data,每个构造函数里都不干啥,只是打印一条信息。
|------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class Data { public: Data() { printf("Data()\n"); } Data(int a) { printf("Data(int a)\n"); } Data(int a, float b) { printf("Data(int a, float b)\n"); } ~Data() { printf("~Data()\n"); } };
|
然后我们为Data写一个工厂函数,像下面这个样子,也比较简单,只是创建一个指针再返回出来。
|-------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5
| template <typename T, typename ...Args> std::shared_ptr<T> CreateData(Args &&...args) { return std::make_shared<T>(std::forward<Args>(args)...); }
|
测试代码如下。
|-----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6 7
| int main(int argc, const char *argv[]) { auto a = CreateData<Data>(); auto b = CreateData<Data>(1); auto c = CreateData<Data>(1,1.5); return 0; }
|
运行之后输出如下,就和预想的一样。
|---------------------|-------------------------------------------------------------------------|
| 1 2 3 4 5 6
| Data() Data(int a) Data(int a, float b) ~Data() ~Data() ~Data()
|
大家可能注意到,CreateData返回的并不是裸指针,而是make_shared出来的shared_ptr,下面是make_shared的代码,有没有觉得很眼熟呢?
|---------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 1 2 3 4 5 6
| template<typename _Tp, typename... _Args> inline shared_ptr<_Tp> make_shared(_Args&&... __args) { typedef typename std::remove_const<_Tp>::type _Tp_nc; return std::allocate_shared<_Tp>(std::allocator<_Tp_nc>(), std::forward<_Args>(__args)...); }
|
没错,make_shared在为shared_ptr构建对象时也用到了完美转发,如果我们再给Data包装一个具有引用计数功能的管理类,就很像是shared_ptr的实现了。
参考链接:https://zhuanlan.zhihu.com/p/275075782