柯里函数的惰性类型推断

Lazy type-inference for curried functions

本文关键字:类型 函数      更新时间:2023-10-16

在以下示例中,对mkPair2调用的类型推断失败:

#include <functional>
template <class A, class B>
struct Pair {
  A left; B right;
};
template <class A, class B>
Pair<A,B> mkPair1 (A left, B right) {
  return (Pair<A,B>) { left, right };
}
template <class A, class B>
std::function<Pair<A,B>(B)> mkPair2 (A left) {
  return [left] (B right) {
    return (Pair<A,B>) { left, right };
  };
}
Pair<int, char> ex1 = mkPair1 (2, 'a');
Pair<int, char> ex2 = mkPair2 (2) ('a');

问题是mkPair2有两个模板参数,但调用(2)只为它提供了其中一个,所以编译器立即举手并决定程序是模棱两可的,即使第二种类型可以从下面的('a')调用中推断出来。

这可以通过手动mkPair2<int,char> (2) ('a')给编译器类型来解决,但是不得不像这样握住编译器的手很快就会变老。

有没有办法诱使编译器继续类型检查,前提是每个类型最终都会被解析?

即使第二种类型可以从以下 ('a'( 调用中推断出来。

是的,可以推断,但C++规则不允许这样做。 类型推断仅发生在函数参数上。 因此,如果您缺少模板参数的函数参数,则需要自己指定它。

也就是说,C++14 的自动返回类型推导和通用 lambda 将使您不必指定任何内容。 您可以将代码重写为

template <class A, class B>
struct Pair {
  A left; B right;
};
template <class A, class B>
auto mkPair1 (A left, B right) {
  return Pair<A,B>{ left, right };
}
template <class A>
auto mkPair2 (A left) {
  return [left] (auto right) {
    return Pair<A, decltype(right)>{ left, right };
  };
}
Pair<int, char> ex1 = mkPair1 (2, 'a');
Pair<int, char> ex2 = mkPair2 (2) ('a');

一切都会为你推断出来。 它还返回 lambda 对象,而不是std::function,因此您可以避免std::function使用的类型擦除的成本。

除了 @NathanOliver 的答案之外,您还可以通过自己创建通用 lambda 的等效项来使其也适用于 C++11:

template <class A>
struct X {
    A left;
    X(A left) : left(left) {}
    template <class B>
    Pair<A,B> operator()(B right) {
        return Pair<A,B>{left, right};
    }
};

然后:

template <class A>
X<A> mkPair2(A left) {
    return X<A>(left);
}