创建函数变量向量时出现“没有匹配的函数供调用”错误

时间:2019-11-28 21:03:51

标签: c++ c++17 variant

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

为什么以下代码无法编译:

#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完全具有我在变体中指定的一种类型的签名,所以一切都很好,不是吗?

2 个答案:

答案 0 :(得分:4)

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

是的,您的lambda是std::function< float (const std::vector<float>&, int) >的有效初始化器。这是由于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(); });

答案 1 :(得分: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::functionconst std::vector<float>&且返回类型为 callable int的构造方法将参与重载解析隐式转换为intfloat。类型不必完全相同。

因此,对于您的lambda来说,这两种重载都是可能的,并且由于每个重载都需要一个用户定义的转换(从lambda类型转换为std::function),因此重载分辨率是不确定的。