能否通过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
相关产品推荐
相关产品推荐

