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

时间:2014-10-03 22:36:52

标签: c++ templates lambda 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);
    }

我可以成功使用WhichOp1:

    int e = WhichOp1(2, [](int i){return i * 2; });

但是对于WhichOp2的类似调用不会编译:

    int d = WhichOp2(2, [](int i){return i * 2; }, [](int i){return i * 3; });

我收到以下错误:

错误C2782:&#39;未知类型chaj :: ops :: WhichOp2(A,OP,OP)&#39; :模板参数&#39; OP&#39;含糊不清

IntelliSense:没有函数模板的实例&#34; chaj :: ops :: WhichOp2&#34;匹配参数列表             参数类型是:(int,lambda [] int(int i) - &gt; int,lambda [] int(int i) - &gt; int)

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

    int b = WhichOp2<int, int(*)(int)>(2, [](int i){return i * 2; }, [](int i){return i * 3; });

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

2 个答案:

答案 0 :(得分:6)

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

由于它们是单独的类型,因此对于编译器而言,它大致就像你曾试图做过类似的事情:

template <class T>
T foo(T a, T b) { return a + b; }

...然后尝试执行以下操作: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());

在这里,由于您自己定义了foobar,很明显它们是两种不同的类型,即使它们都定义了相同类型的operator()。 Lambda表达式完全相同,除了有关lambda表达式定义的类名称的次要(和不相关)细节。

答案 1 :(得分:4)

C ++标准的5.1.2/3 ([expr.prim.lambda])章说:

  

lambda-expression的类型(也是闭包对象的类型)是一个唯一的,未命名的nonunion类类型

所以这一行中的两个lambdas

WhichOp2(2, [](int i){return i * 2; }, [](int i){return i * 3; });

有不同的类型,即使它们看起来一样。如果您希望WhichOp2像这样工作,则必须使用不同的操作类型声明它:

template <class A, class OP1, class OP2>
auto WhichOp2(A argument, OP1 firstOp, OP2 secondOp)->decltype(firstOp(argument) + secondOp(argument)) {
    return firstOp(argument) + secondOp(argument);
}