【问题标题】:How do I force the compiler to recognize lambda type in templated function?如何强制编译器识别模板化函数中的 lambda 类型?
【发布时间】:2015-03-23 02:20:30
【问题描述】:

这是一个完整的小程序。对test1()test2() 的前三个调用编译并正常运行,对test2() 的最后一个调用没有编译。如何在不指定调用类型的情况下让编译器识别对 test2() 的调用?

#include <functional>
#include <iostream>

// using function pointer
template <typename T>
T test1(T arg, T (*fnptr)(T)) {
  return fnptr(arg);
}

// using a lambda
template <typename T>
T test2(T arg, std::function<T (T)> mapfn) {
  return mapfn(arg);
}

int dbl(int v) {
  return 2 * v;
}

int main() {
  // v1a: compiles, runs without error
  int v1a = test1<int>(11, dbl);
  std::cout << v1a << std::endl;

  // v2a: compiles, runs without error
  int v2a = test2<int>(11, [=](int arg) { return 2 * arg; });
  std::cout << v2a << std::endl;

  // v1b (without template type): compiles, runs without error
  int v1b = test1(11, dbl);
  std::cout << v1b << std::endl;

  // v2a (without template type): doesn't compile: no matching fn
  int v2b = test2(11, [=](int arg)->int { return 2 * arg; });
  std::cout << v2b << std::endl;

  return 0;
}

编译器生成以下消息:

$ g++ -O3 -Wall -std=c++11 -o sketch_tiny sketch_tiny.cpp 
sketch_tiny.cpp:34:13: error: no matching function for call to 'test2'
  int v2b = test2(11, [=](int arg)->int { return 2 * arg; });
            ^~~~~
sketch_tiny.cpp:12:3: note: candidate template ignored: could not match
      'function<type-parameter-0-0 (type-parameter-0-0)>' against '<lambda at
      sketch_tiny.cpp:34:23>'
T test2(T arg, std::function<T (T)> mapfn) {
  ^
1 error generated.

有没有办法让编译器在不使用test2&lt;int&gt;(...) 类型说明符的情况下识别对test2(...) 的调用?

不管怎样,编译环境如下:

$ g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 6.0 (clang-600.0.57) (based on LLVM 3.5svn)
Target: x86_64-apple-darwin14.1.0
Thread model: posix

【问题讨论】:

  • 不同的是,lambda不是std::function,而是dbl衰减为函数指针。
  • @T.C.为什么不能只从第一个参数中进行推断?
  • T.C.:我同意最初的问题类似于stackoverflow.com/questions/9998402/…。我已经重新措辞了这个问题以提出真正的问题:如何强制编译器匹配模板函数中的 lambda? 虽然我现在知道答案(使用具有两种类型的模板,即template &lt;typename T, typename MapFn&gt;),这个问题应该重新打开,因为答案通常很有用(你真的必须挖掘才能在另一个问题中找到答案)。

标签: c++ templates c++11 lambda function-pointers


【解决方案1】:

模板参数推导只查看一组非常有限的隐式转换。 lambda 不是std::function,因此模板参数推导失败。

通常有两种方法可以解决这个问题:

  1. 将 lambda 的类型作为单独的模板参数:

    template <typename T, typename F>
    T test2(T arg, F mapfn) {
      return mapfn(arg);
    }
    

    这是最有效的方法。

  2. 如果出于某种原因您真的想使用std::function 并支付相关的类型擦除成本,您可以将std::function&lt;T(T)&gt; 放入非推断上下文中。

    template <typename T> struct identity { using type = T; };
    template <typename T> using non_deduced = typename identity<T>::type;
    
    template <typename T>
    T test2(T arg, non_deduced<std::function<T (T)>> mapfn) {
      return mapfn(arg);
    }
    

【讨论】:

  • 使用 lambda 的类型作为模板参数(第一个解决方案)对我来说就像一个冠军。如果你这样做,你可以将一个 lambda 或一个函数指针传递给 test2() - 一个不错的奖励。
猜你喜欢
  • 2020-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多