【问题标题】:"No matching function for call" error when creating a vector of function variants创建函数变体向量时出现“调用没有匹配函数”错误
【发布时间】:2019-11-28 21:03:51
【问题描述】:

我正在尝试创建一个std::vector,它可以使用std::variant 保存不同签名的std::function 对象。

为什么下面的代码不能编译:

#include <functional>
#include <variant>
#include <vector>

int main()
{
  std::vector<std::variant<
      std::function< int (const std::vector<float>&, int) >,
      std::function< float (const std::vector<float>&, int) >
  >> func_vector;

  func_vector.emplace_back( [] (const std::vector<float>& ret, int index) { return ret.size(); });

  return 0;
}

问题发生在emplace_back() 期间。编译它会给出一长串错误,列出的第一个是:

error: no matching function for call to ‘std::variant<std::function<int(const std::vector<float, std::allocator<float> >&, int)>, std::function<float(const std::vector<float, std::allocator<float> >&, int)> >::variant(main()::<lambda(const std::vector<float>&, int)>)’
  { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

它说它找不到匹配的函数,但究竟是什么调用?

我试图放置的 lambda 具有我在变体中指定的类型之一的签名,所以一切都应该没问题,不是吗?

【问题讨论】:

    标签: c++ c++17 variant


    【解决方案1】:

    emplace_back 应该将 lambda 直接转发到变量初始化。并且有一个转换构造函数可以从任何可转换为成员类型的参数初始化变体的成员。然而,问题是变体的两个成员都可以从这个 lambda 初始化,从而产生歧义。

    是的,您的 lambda 是 std::function&lt; float (const std::vector&lt;float&gt;&amp;, int) &gt; 的有效初始化程序。这是由于std::function 执行类型擦除的方式。它将它持有的可调用的结果转换为它指定的返回类型。可调用对象必须能够接受 std::function 的参数列表。

    为了说明这一点,如果我们要为std::function 类型之一添加第三个参数,

    std::vector<std::variant<
      std::function< int (const std::vector<float>&, int) >,
      std::function< float (const std::vector<float>&, int, int) >
    >> func_vector;
    

    然后是would be no ambiguity。 lambda 现在是只有一个变体成员的有效初始化程序。

    解决方法是要么转换为您希望保留的确切函数类型,要么告诉放置的变体它应该初始化哪个选项,例如:

    func_vector.emplace_back( std::in_place_index<0>, [] (const std::vector<float>& ret, int ) { return ret.size(); });
    

    【讨论】:

      【解决方案2】:

      std::variant 的转换构造函数的行为类似于重载解析,以确定要构造的类型。

      如果你有两个功能

      void f(std::function< int (const std::vector<float>&, int) >);
      void f(sstd::function< float (const std::vector<float>&, int) >);
      

      然后调用

      f([] (const std::vector<float>& ret, int index) { return ret.size(); })
      

      也会有歧义,因为std::function 的构造函数参与重载决议,如果参数是可调用 类型为const std::vector&lt;float&gt;&amp;int 并且返回类型可隐式转换为intfloat。类型不需要完全相同。

      因此,对于您的 lambda,这两种重载都是可能的,并且由于它们中的每一个都需要一个用户定义的转换(从 lambda 类型到 std::function),因此重载决议是不明确的。

      【讨论】:

      • 非常感谢您的出色回答!如果你能展示如何解决这个问题,那就更好了。
      • @Alex 一个不需要修改emplace_back 使用的干净解决方案可能是围绕std::function 编写一个包装类,仅当类型完全匹配时才启用其构造函数。我现在没有时间写出来。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-05-05
      • 1970-01-01
      • 1970-01-01
      • 2012-12-28
      • 2016-01-18
      • 2015-03-31
      相关资源
      最近更新 更多