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

能否通过C++17推导指南实现std::make_tuple的全部功能?

给std::tuple写自定义推导指南,复刻std::make_tuple的引用推导逻辑

你说得对,C++17的类模板推导确实让std::make_tuple看起来有点多余,但它有个非常实用的特性:当你传入std::reference_wrapper(比如std::ref或std::cref返回的对象)时,它会自动推导成对应的引用类型,而不是把reference_wrapper本身作为tuple的元素类型。默认的std::tuple构造推导指南可做不到这一点——它会直接把reference_wrapper当作元素类型。

那怎么给std::tuple写自定义推导指南,实现和make_tuple一模一样的推导逻辑呢?咱们一步一步来:

第一步:写个小工具处理单个参数的类型转换

首先我们需要一个模板元工具,用来把单个输入类型转换成make_tuple会推导的类型:

  • 如果输入是std::reference_wrapper<T>,就转换成T&(如果是const T的包装,就转成const T&)
  • 否则就对类型做std::decay(和make_tuple一样,处理数组、左值引用等情况)

代码实现如下:

#include <tuple>
#include <functional>
#include <type_traits>

// 基础模板:对普通类型做decay
template<typename T>
struct unwrap_ref_decay {
    using type = std::decay_t<T>;
};

// 特化版本:处理std::reference_wrapper,解包成引用
template<typename T>
struct unwrap_ref_decay<std::reference_wrapper<T>> {
    using type = T&;
};

// 辅助别名,简化代码里的使用
template<typename T>
using unwrap_ref_decay_t = typename unwrap_ref_decay<T>::type;

第二步:给std::tuple写自定义推导指南

有了上面的工具,我们就可以写推导指南了。这里要用转发引用(Args&&)来定义,这样能确保我们的推导指南比标准库的默认指南优先级更高,避免歧义:

// 自定义推导指南:复刻make_tuple的推导逻辑
template<typename... Args>
std::tuple(Args&&... args) -> std::tuple<unwrap_ref_decay_t<Args>...>;

这个指南的逻辑很简单:对每个传入的参数,用unwrap_ref_decay_t处理后,作为std::tuple的模板参数。

第三步:测试验证效果

咱们写点测试代码,看看这个推导指南是不是符合预期:

struct A { int i; };

int main() {
    A a{10};
    const A ca{20};

    // 测试std::ref:推导成A&,可以修改原对象
    auto t1 = std::tuple{std::ref(a)};
    static_assert(std::is_same_v<decltype(t1), std::tuple<A&>>);
    t1.get<0>().i = 100; // 原a的i会变成100,说明是引用

    // 测试std::cref:推导成const A&,不能修改原对象
    auto t2 = std::tuple{std::cref(ca)};
    static_assert(std::is_same_v<decltype(t2), std::tuple<const A&>>);
    // t2.get<0>().i = 200; // 编译错误,符合const引用的特性

    // 测试普通右值对象:推导成值类型A
    auto t3 = std::tuple{A{15}};
    static_assert(std::is_same_v<decltype(t3), std::tuple<A>>);

    // 测试混合参数:同时有引用包装、普通值
    auto t4 = std::tuple{std::ref(a), 42, std::cref(ca)};
    static_assert(std::is_same_v<decltype(t4), std::tuple<A&, int, const A&>>);
}

额外说明

  • 这个推导指南完全复刻了std::make_tuple的行为:既处理了reference_wrapper的引用推导,也对普通参数做了decay转换(比如数组会转成指针,左值引用会被剥离等)。
  • 用转发引用定义推导指南是关键,这样编译器会优先选择我们的指南,而不是标准库的默认推导指南,避免了推导歧义。

内容的提问来源于stack exchange,提问作者Dev Null

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 09:01:02