【问题标题】:Two lambdas passed to template function makes type deduction of parameter ambiguous -- why?传递给模板函数的两个 lambda 使参数的类型推导模棱两可——为什么?
【发布时间】:2014-11-29 00:42:14
【问题描述】:

我有一个模板,如果我将一个 lambda 传递给它,它就可以工作,但是在一个相关模板中,它需要两个 lambdas 映射到同一个模板化类型,它无法推断出该类型,并且 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: '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; });

所以我的问题只是试图更好地理解正在发生的事情——为什么编译器在将两个相似的 lambdas 传递给一个通用模板参数下的模板时不能解决歧义?智能感知错误似乎将 lambdas 的类型映射到相同的类型。如果这是特定于编译器的,我不会感到惊讶,但如果有人看到这适用于他们的编译器,我很想知道。

【问题讨论】:

    标签: c++ templates lambda ambiguity


    【解决方案1】:

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

    由于它们是不同的类型,因此对于编译器来说,就好像您试图执行以下操作一样:

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

    ...然后尝试执行以下操作:auto x = foo(1, 2.0); 由于您传递了intdouble,编译器无法确定Tint 还是double .

    两种情况下的修复方法相同:如果您可能使用 lambda,请为每种情况指定一个单独的模板参数。

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

    struct foo { 
        bool operator()();
    };
    
    struct bar {
        bool operator()();
    };
    
    template <class T>
    void baz(T a, T b) { /* ... */ }
    
    baz(foo(), bar());
    

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

    【讨论】:

      【解决方案2】:

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

      lambda 表达式的类型(也是闭包对象的类型)是唯一的、未命名的非联合类类型

      所以这行中的两个 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);
      }
      

      【讨论】:

      • 我不仅考虑了两种类型的方法,还使用了 OP1 和 OP2 作为标识符:-p 我担心的是 decltype 尾随返回类型,它只绑定到 OP1 的返回类型,但是模板会让 OP1 和 OP2 不同。
      • @CharlesJ.Daniels 你是对的,你应该使用 decltype(firstOp(argument) + secondOp(argument)) 作为尾随返回类型,请参阅更新的答案。
      猜你喜欢
      • 2020-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多