首页 > 解决方案 > 在实例化之前推断函数模板的返回类型

问题描述

这是递归函数的返回类型推导的后续问题。

template <class F>
struct y_combinator {
    F f; // the lambda will be stored here

    // a forwarding operator():
    template <class... Args>
    decltype(auto) operator()(Args&&... args) const {
        // we pass ourselves to f, then the arguments.
        // [edit: Barry] pass in std::ref(*this) instead of *this
        return f(std::ref(*this), std::forward<Args>(args)...);
    }
};
// deduction guide
template <class F> y_combinator(F) -> y_combinator<F>;

基本上,y_combinator允许人们更轻松地编写递归 lambda 表达式(例如,无需删除 a std::function)。

在这个问题中,考虑[dcl.spec.auto]/10不适用的情况(即递归调用之前没有返回语句)。

// this does not compile
y_combinator{
// lets call the lambda expression #1
[](auto g, int i) {
  for (int j = i; j < 10; ++j) {
    g(j + 1);
  }
}}(1);

活生生的例子

根据ecatmur对原始问题的回答,上面的代码失败了,因为

y_combinator<lambda #1>::operator()<int> instantiates
  -> [lambda #1]::operator()<y_combinator<lambda #1>> instantiates
       -> need to know return type of y_combinator<lambda #1>::operator()<int> at the point of g(j+1);
       -> fail!

但是,编译器很可能知道 的返回类型在第一次实例化F::operator()之前。voidy_combinator<lambda #1>::operator()<int>

  1. 为什么在构造F::operator()临时对象时不推导出lambda #1 的返回类型(以及因此的返回类型)?y_combinator

  2. -> void是否可以在不向lambda #1添加尾随返回类型的情况下编译代码?

标签: c++

解决方案


为了推断返回类型,编译器必须实例化泛型 lambda。它做到了,代码格式不正确,因为他无法推断出g(j+1).

为了能够编译此代码,编译器应该进行归纳:

  1. 让我们假设 的返回值g(j+1)使得通用 lambda 调用运算符实例化格式良好。
  2. 所以泛型 lambda 实例化成功,其返回类型为 void
  3. 所以 y_combinator 调用运算符返回类型为 void
  4. 所以确实第一个假设是正确的!

也许当编译器能够做到这一点时,计算机将自己编码!

所以不,不可能不使用尾随返回类型。或者做得更好,就用一个函数。Lambda 是导致模板实例化爆炸的恶魔。


推荐阅读