You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为带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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 20:42:29