为什么这种对重载函数的调用不明确

Why is this call of overloaded function ambiguous?

本文关键字:调用 不明确 函数 重载 为什么      更新时间:2023-10-16

为什么这个构造函数的调用是不明确的?

#include <functional>
class A {
    std::function<int(void)> f_;
    std::function<float(void)> g_;
public:
    A(std::function<int(void)> f) { f_ = f; }
    A(std::function<float(void)> g) { g_ = g; }
    ~A() {}
};
int main()
{
    A a([](){ return (int)1; });
    return 0;
}

请注意类型转换。

有没有办法告诉编译器使用哪个构造函数重载?

这是标准的缺陷。请参阅 DR 2132:

请考虑以下事项:

#include <functional>
void f(std::function<void()>) {}
void f(std::function<void(int)>) {}
int main() {
  f([]{});
  f([](int){});
}

对 f 的调用是模棱两可的。显然是因为 从 lambda 转换为std::function序列是 相同。该标准指定函数对象给定 std::function "对于参数类型应为可调用 (20.8.11.2) ArgTypes并返回类型 R ."它并没有说如果这不是 在这种情况下,构造函数不是重载集的一部分。

尝试改用函数指针作为参数:

A(int f()) { f_ = f; }
A(float g()) { g_ = g; }

因为您要传递的内容与类型不匹配,所以我们进入转换序列以找到要使用的重载。 两个版本的函数都可以从返回 int 的 lambda 对象隐式创建。 因此,编译器无法决定选择创建哪个;尽管直觉上似乎很明显,C++中的规则不允许这样做。

编辑:

从袖口上注销,但我认为这可以解决问题:

template < typename Fun >
typename std::enable_if<std::is_same<typename std::result_of<Fun()>::type, int>::value>::type f(Fun f) ...

template < typename Fun >
typename std::enable_if<std::is_same<typename std::result_of<Fun()>::type, double>::value>::type f(Fun f) ...

等。。。 或者,您可以使用标记调度:

template < typename Fun, typename Tag >
struct caller;
template < typename T > tag {};
template < typename Fun >
struct caller<Fun, tag<int>> { static void call(Fun f) { f(); } };
// etc...
template < typename Fun >
void f(Fun fun) { caller<Fun, typename std::result_of<Fun()>>::call(fun); }
template<class F,
  class R=std::result_of_t<F&()>,
  std::enable_if_t<std::is_convertible<R,int>{}&&(!std::is_convertible<R,float>{}||std::is_integral<R>{}),int> =0
>
A(F&&f):A(std::function<int()>(std::forward<F>(f))){}
template<class F,
  class R=std::result_of_t<F&()>,
  std::enable_if_t<std::is_convertible<R,float>{}&&(!std::is_convertible<R,int>{}||std::is_floating_point<R>{}, int> =0
>
A(F&&f):A(std::function<float()>(std::forward<F>(f))){}
A(std::function<int()> f) { f_ = f; }
A(std::function<float()> g) { g_ = g; }

这里我们采用模棱两可的情况,并将积分分派给 int 重载,将非浮点分派给浮点重载。

它既可以转换为两者,但既不是浮点也不是积分的情况仍然模棱两可。 正如他们应该的那样。

sfina 条款可能很棘手。 如果不起作用,请替换:

  std::enable_if_t<std::is_convertible<R,float>{}&&(!std::is_convertible<R,int>{}||std::is_floating_point<R>{}, int> =0

  class=std::enable_if_t<std::is_convertible<R,float>{}&&(!std::is_convertible<R,int>{}||std::is_floating_point<R>{}>

以及其他CTOR类似。 例如,这可能适用于 MSVC。

由于 2015 年缺乏表达 sfinae,MSVC 可能需要完全进行标签调度。