【问题标题】:Template argument deduction from lambda从 lambda 推导模板参数
【发布时间】:2017-05-08 08:57:09
【问题描述】:

我正在尝试编写一个函数,该函数基本上将在一种类型上模板化的类的实例转换为在第二种类型上模板化的同一类的实例。我想避免在调用函数时显式声明模板类型。

这是我正在尝试做的一个最小的可编译示例:

template<class T> class container {};

template <class A, class B, template <class C> class Container>
Container<B> transform(Container<A> c, B(func)(A))
{
  return Container<B>{};
}

int do_something(int in)
{
  return in + 1;
}

int main()
{
  container<int> c{};

  //this one doesn't work:
  //transform(c, [](int in) -> int { return in + 1; });
  //these are fine:
  transform<int, int>(c, [](int in) -> int { return in + 1; });
  transform(c, do_something);

  return 0;
}

取消注释掉第一个 transform 调用会导致编译错误:

Visual Studio 2017:

error C2784: 'Container<B> transform(Container<A>,B (__cdecl *)(A))': could not deduce template argument for 'B (__cdecl *)(A)' from 'test::<lambda_afc081691b59f849887abca17e74b763>'

默认使用哪个版本的 g++ coliru.stacked-crooked.com:

main.cpp:4:14: note:   template argument deduction/substitution failed:
main.cpp:18:52: note:   mismatched types 'B (*)(A)' and 'main()::<lambda(int)>'
   transform(c, [](int in) -> int { return in + 1; });
                                                   ^

这是否意味着编译器不可能推断出 lambda 的签名,即使它已经像这样明确定义了?

我知道我可以像这样重写我的转换函数:

template <class A, template <class C> class Container, class F>
auto transform(Container<A> c, F func)->Container<decltype(func(A{}))>
{
  return Container<decltype(func(A{}))>{};
}

但现在函数签名的可读性降低了,如果我提供了不合适的函数,我收到的错误消息非常不友好。使用std::function&lt;B(A)&gt; 也无济于事。

有没有办法在不显式添加模板类型的情况下将更严格指定的函数参数与 lambdas 一起使用?

【问题讨论】:

    标签: c++ templates lambda


    【解决方案1】:

    您需要将无捕获 lambda 转换为执行操作的静态函数。这种转换实际上可以通过应用一元 + 运算符相当容易地调用。

    transform(c, +[](int in) -> int { return in + 1; });
    

    由于无捕获 lambda 的闭包类型具有到 ret(*)(params) 的转换运算符,因此编译器将在遇到 + 时调用它。那是因为您实际上可以将+ 应用于指针类型。

    [expr.unary.op/7]

    一元+运算符的操作数应具有算术、无范围枚举、或指针类型,结果是参数的值。对整数或枚举操作数执行整数提升。结果的类型是提升操作数的类型。

    【讨论】:

    • 我以前从未听说过+ 技巧。漂亮。 Coliru 的 g++ 可以接受,但是 VS2015 和 VS2017 失败并出现一个有趣的 'operator +' is ambiguous 错误,无法区分 &lt;lambda_typedef_cdecl&gt;&lt;lambda_typedef_vectorcall&gt;,所以很遗憾我不能使用这个技巧。
    • @Rook - MSVC 当然会拒绝它...抱歉,我不知道如何解决这个问题。
    • 既然参数是一个函数,不应该隐式地把 lambda 转换成函数指针?
    • @bolov - 不在推断的上下文中。它在标准中的某个位置,但我目前无法找到它。
    • 刚刚在这里找到了对相同解决方案的另一个参考:stackoverflow.com/questions/25038534/… 似乎 MSVC 多年来一直无法做到这一点。有点失望。
    【解决方案2】:

    BA 不能在 B(func)(A) 中从 lambda 推导出来。

    您可以将模板更改为更通用,例如:

    template <template <typename...> class Container, typename Ts...>
    auto transform(const Container<Ts...>& c, F f)
    -> Container<std::decay_t<decltype(f(*c.begin())>>
    {
        return {};
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多