如何避免为禁用universal references的函数编写L/R值引用全排列?
问题:维持模板函数类型规范同时避免引用排列组合
需求与限制
- 必须维持模板函数
bar的完整类型规范(即明确模板类如X/Y/Z及其类型参数如float/double/int) - 避免编写
bar所有左值/右值引用的排列组合(参数数量增加时,排列数会指数级增长) - 当前无法直接使用万能引用(
T&&)的写法
代码示例
#include <iostream> #include <vector> // 直接查看bar函数 template<typename DT> struct X { DT data; X(DT data) : data(data * 2) {} template<typename VT> float foo(VT value) { return data + value; } }; template<typename DT> struct Y { DT data; Y(DT data) : data(data * 4) {} template<typename VT> float foo(VT value) { return value * data; } }; template<typename DT> struct Z { DT data; Z(DT data) : data(data * 8) {} template<typename VT> float foo(VT value) { return (value + 1) * (data + 1); } }; // 目标函数 template <template<typename> typename XT, typename AT, template<typename> typename YT, typename BT, template<typename> typename ZT, typename CT> float bar(XT<AT>&& a, YT<BT>&& b, ZT<CT>&& c) // 不允许使用万能引用 { XT<float> a_result(b.data - c.data); YT<float> b_result(a.data - c.data); XT<float> c_result(a.data + b.data); return a_result.foo(a.data) + b_result.foo(b.data) + c_result.foo(c.data); } int main() { X<float> x(1.); Y<double> y(2.); Z<int> z(3); double result = bar(x, y, z); std::cout << result << std::endl; }
编译错误
无法执行程序 编译器返回:1 编译器错误输出: <source>: 在函数'int main()'中: <source>:52:23: 错误:无法将类型'X<float>&&'的右值引用绑定到类型'X<float>'的左值 52 | double result = bar(x, y, z); | ^ <source>:39:20: 注:正在初始化'float bar(XT<AT>&&, YT<BT>&&, ZT<CT>&&) [with XT = X; AT = float; YT = Y; BT = double; ZT = Z; CT = int]'的参数1 39 | float bar(XT<AT>&& a, YT<BT>&& b, ZT<CT>&& c) { // 不允许使用万能引用 | ~~~~~~~~~^
解决方案
可以通过类型萃取模板提取参数的模板类和类型参数,结合万能引用实现需求,无需编写所有引用排列:
修改后的代码
#include <iostream> #include <utility> // 用于std::forward template<typename DT> struct X { DT data; X(DT data) : data(data * 2) {} template<typename VT> float foo(VT value) { return data + value; } }; template<typename DT> struct Y { DT data; Y(DT data) : data(data * 4) {} template<typename VT> float foo(VT value) { return value * data; } }; template<typename DT> struct Z { DT data; Z(DT data) : data(data * 8) {} template<typename VT> float foo(VT value) { return (value + 1) * (data + 1); } }; // 类型萃取模板:提取模板类和其类型参数 template<typename T> struct extract_template; // 处理原始类型(XT<AT>) template<template<typename> typename XT, typename AT> struct extract_template<XT<AT>> { template<typename U> using template_type = XT<U>; using value_type = AT; }; // 处理左值引用(XT<AT>&) template<template<typename> typename XT, typename AT> struct extract_template<XT<AT>&> : extract_template<XT<AT>> {}; // 处理右值引用(XT<AT>&&) template<template<typename> typename XT, typename AT> struct extract_template<XT<AT>&&> : extract_template<XT<AT>> {}; // 新版bar函数 template<typename A, typename B, typename C> float bar(A&& a, B&& b, C&& c) { // 萃取每个参数的模板类和类型参数 using XT = typename extract_template<A>::template template_type; using AT = typename extract_template<A>::value_type; using YT = typename extract_template<B>::template template_type; using BT = typename extract_template<B>::value_type; using ZT = typename extract_template<C>::template template_type; using CT = typename extract_template<C>::value_type; // 原函数逻辑不变 XT<float> a_result(b.data - c.data); YT<float> b_result(a.data - c.data); XT<float> c_result(a.data + b.data); return a_result.foo(a.data) + b_result.foo(b.data) + c_result.foo(c.data); } int main() { X<float> x(1.); Y<double> y(2.); Z<int> z(3); // 左值参数可以正常传递 double result = bar(x, y, z); std::cout << result << std::endl; // 右值参数也能正常传递 double result_rvalue = bar(X<float>(4.), Y<double>(5.), Z<int>(6)); std::cout << result_rvalue << std::endl; }
方案说明
- 类型萃取模板:
extract_template负责从参数类型(无论左值/右值引用)中提取出原始的模板类(如X)和类型参数(如float),保证了你需要的完整类型规范。 - 万能引用:
A&&、B&&、C&&会自动推导参数的引用类型,无论是左值还是右值都能正确绑定,避免了手动编写所有引用排列组合的麻烦。 - 原有逻辑保留:函数内部的业务逻辑完全不需要修改,只需要通过萃取得到所需的模板类和类型参数即可。
这种方法既满足了你的两个核心目标,又避免了参数数量增加时的代码爆炸问题,比编写所有引用排列要简洁得多。
内容的提问来源于stack exchange,提问作者user8217919
相关产品推荐
相关产品推荐

