如何为带std::source_location默认参数的变参模板实现完美转发?
使用std::source_location追踪对象创建位置的变参模板实现问题
问题描述
我希望用std::source_location追踪对象的创建位置,现有简化代码仅支持单参数构造的类型,想要扩展为支持任意参数的变参模板,但默认参数std::source_location loc会和变参模板的参数推导冲突。
现有代码:
#include <iostream> #include <memory> #include <source_location> template <typename Underlying> struct Tracked { Underlying val; std::source_location loc; }; template <typename Underlying, typename T> auto makeTracked(T&& t, std::source_location loc = std::source_location::current()) { // make_unique为简化写法,实际代码更复杂 // 关键在于此处存在额外函数调用 return std::make_unique<Tracked<Underlying>>(Underlying{ std::forward<T>(t) }, std::move(loc)); } int main() { auto p = makeTracked<std::string>("abc"); std::cout << p->val << "@" << p->loc.line() << std::endl; // 预期用法: // auto p1 = makeTracked<std::tuple<int, int, int>>(3, 4, 5); }
已尝试的方案均不适用:
- 改为结构体方案:无法部分推导模板参数,且需要特定返回类型,不符合需求;
- 指定首个参数类型:不符合调用习惯;
- 手动扩展参数数量:不够灵活,需要预先知道最大参数个数。
需要实现支持任意数量参数、完美转发,同时保留std::source_location默认参数的方案。
解决方案
可以通过将变参参数包放在前面,把std::source_location参数放在最后并保留默认值,同时利用C++17的模板参数推导和完美转发来实现。核心是让编译器先推导变参参数,再使用默认的位置参数,避免推导冲突。
修改后的完整代码如下:
#include <iostream> #include <memory> #include <source_location> #include <tuple> template <typename Underlying> struct Tracked { Underlying val; std::source_location loc; // 为Tracked添加变参构造函数,完美转发参数给Underlying template <typename... Args> Tracked(Args&&... args, std::source_location loc_) : val(std::forward<Args>(args)...), loc(std::move(loc_)) {} }; template <typename Underlying, typename... Args> auto makeTracked(Args&&... args, std::source_location loc = std::source_location::current()) { return std::make_unique<Tracked<Underlying>>( std::forward<Args>(args)..., std::move(loc) ); } int main() { auto p = makeTracked<std::string>("abc"); std::cout << p->val << "@" << p->loc.line() << std::endl; // 预期用法现在可以正常工作 auto p1 = makeTracked<std::tuple<int, int, int>>(3, 4, 5); std::cout << std::get<0>(p1->val) << "," << std::get<1>(p1->val) << "," << std::get<2>(p1->val) << "@" << p1->loc.line() << std::endl; }
关键说明
- 参数顺序调整:将变参参数包
Args&&... args放在前面,std::source_location参数放在最后并设置默认值。编译器会优先推导变参参数,最后使用默认的位置参数,彻底避免推导冲突。 - Tracked构造函数优化:给
Tracked添加变参构造函数,通过std::forward完美转发所有参数给Underlying的构造函数,确保支持任意构造参数的类型。 - 完美转发保留:全程使用
std::forward<Args>(args)...传递参数,保证参数的左值/右值属性被正确保留,避免不必要的拷贝开销。
这种方案完全满足需求:支持任意数量的构造参数、完美转发,同时正确追踪对象创建的源代码位置,且不需要额外的结构体包装或手动扩展参数数量。
内容的提问来源于stack exchange,提问作者Weijun Zhou
相关产品推荐
相关产品推荐

