万能引用 + MLIR 源代码示例解析

万能引用是让函数同时接受左值(变量原始类型,引用)和右值(临时变量,std::move 返回值)作为引用形式的参数的一种方法。

这是一段来自 MLIR 项目的源代码,用于遍历 IR 对其执行操作:

template <WalkOrder Order = WalkOrder::PostOrder,typename Iterator = ForwardIterator, typename FnT,typename RetT = detail::walkResultType<FnT>>
std::enable_if_t<llvm::function_traits<std::decay_t<FnT>>::num_args == 1,RetT>
walk(FnT &&callback) {return detail::walk<Order, Iterator>(this, std::forward<FnT>(callback));
}

该函数接受四个模板参数,分别是:

  • Order非类型模板参数(NTTP),通过传入枚举类 WalkOrder 控制函数行为

  • Iterator,控制遍历时从第一个访问到最后一个子节点(Iterator),还是反之(ReverseIterator

  • FnT,执行实际操作的可调用对象(Callable),可以是普通函数,函数指针,函数左/右值引用,Lambda 表达式,Funtor(重载了操作符 () 的类或结构体),std::bind 绑定表达式,std::function<R(Args...)> 类型擦除包装器

  • RetT,传入的函数的返回类型

上述代码中通过使用 std::forward<FnT>(callback) 通过 FnT 保留的函数原始类型,实现对参数的无损转发。

walk(FnT &&callback) 这里就是万能引用,无论传入的参数是什么类型都可以转化成引用形式:

  • 实参:X 类型左值表达式(具名变量 X x、右值引用变量名 rr 等一切左值)

    推导规则:万能引用特殊推导 → FnT = X&
    引用折叠:FnT&& = X& && = X&
    示例:

    int x;
    walk(x); // x 是int左值表达式
    int&& rr = std::move(x);
    walk(rr); // rr 变量类型int&&,但表达式rr是左值,同样走本条
    
  • 实参:X 类型右值表达式(字面量、临时对象、std::move(变量)

    推导规则:FnT = X(裸类型,不带引用)
    引用折叠:FnT&& = X && = X&&
    示例:

    walk(10); // 字面量纯右值
    walk(std::move(x)); // move生成亡值(右值)
    walk(std::move(rr));
    walk(MyOp{}); // 临时对象纯右值