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

如何避免为禁用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;
}

方案说明

  1. 类型萃取模板:extract_template负责从参数类型(无论左值/右值引用)中提取出原始的模板类(如X)和类型参数(如float),保证了你需要的完整类型规范。
  2. 万能引用:A&&、B&&、C&&会自动推导参数的引用类型,无论是左值还是右值都能正确绑定,避免了手动编写所有引用排列组合的麻烦。
  3. 原有逻辑保留:函数内部的业务逻辑完全不需要修改,只需要通过萃取得到所需的模板类和类型参数即可。

这种方法既满足了你的两个核心目标,又避免了参数数量增加时的代码爆炸问题,比编写所有引用排列要简洁得多。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 05:05:00