传递给模板函数的两个lambda使参数的类型推导变得模糊 - 为什么?

Cha*_*els 4 c++ lambda templates ambiguity

我有一个模板,如果我传递一个lambda,但在一个相关的模板,其中两个lambda映射到相同的模板类型,它不能推断该类型,并且MSVC++ Express 2013抱怨模板参数是不明确的.为了清楚起见,这里没有超载(或专业化) - 下面的两个例子是唯一具有这些标识符的实体.以下是模板,它只是在参数上应用可调用对象并返回结果:

    template <class A, class OP>
    auto WhichOp1(A argument, OP firstOp)->decltype(firstOp(argument)) {
        return firstOp(argument);
    }

    template <class A, class OP>
    auto WhichOp2(A argument, OP firstOp, OP secondOp)->decltype(firstOp(argument)) {
        return firstOp(argument) + secondOp(argument);
    }
Run Code Online (Sandbox Code Playgroud)

我可以像这样成功使用WhichOp1:

    int e = WhichOp1(2, [](int i){return i * 2; });
Run Code Online (Sandbox Code Playgroud)

但类似的调用WhichOp2将无法编译:

    int d = WhichOp2(2, [](int i){return i * 2; }, [](int i){return i * 3; });
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

错误C2782:'unknown-type chaj :: ops :: WhichOp2(A,OP,OP)':模板参数'OP'不明确

IntelliSense:没有函数模板实例"chaj :: ops :: WhichOp2"匹配参数列表参数类型是:(int,lambda [] int(int i) - > int,lambda [] int(int i) - > int )

我收集到的是,它根本不能将第一个lambda与第二个lambda一起使用,并在两者之间确定OP的类型应该是什么.如果我明确地实例化,它可以很好地解决歧义:

    int b = WhichOp2<int, int(*)(int)>(2, [](int i){return i * 2; }, [](int i){return i * 3; });
Run Code Online (Sandbox Code Playgroud)

所以我的问题只是试图更好地理解发生了什么 - 为什么编译器在将两个相似的lambda传递给公共模板参数下的模板时不能解决歧义?intellisense错误似乎将lambda的类型映射到相同的类型.如果这是特定于编译器的话,我不会感到惊讶,但是如果有人发现它适用于他们的编译器,我有兴趣知道.

Jer*_*fin 6

每个lambda定义一个唯一的类型.即使它们采用相同的参数并返回相同的类型,两个单独的lambda仍然是两个独立的类型.

因为它们是单独的类型,所以对于编译器来说,它大致就像你试图做的那样:

template <class T>
T foo(T a, T b) { return a + b; }
Run Code Online (Sandbox Code Playgroud)

......然后试着这样做:auto x = foo(1, 2.0);既然你已经通过了intdouble,编译器不能确定是否Tintdouble.

在两种情况下,修复都是相同的:如果您可能正在使用lambdas,请为每个修复指定单独的模板参数.

请注意,这也不是lambdas独有的.如果使用显式定义的函数对象,也会发生同样的情况.例如:

struct foo { 
    bool operator()();
};

struct bar {
    bool operator()();
};

template <class T>
void baz(T a, T b) { /* ... */ }

baz(foo(), bar());
Run Code Online (Sandbox Code Playgroud)

在这里,因为你已经定义foobar你自己,这是很明显,他们是两种不同类型的,即使他们都定义了一个operator()相同类型的.Lambda表达式完全相同,除了有关lambda表达式定义的类名称的次要(和不相关)细节.