我想知道是否有人知道为什么以下示例无法编译并给出对重载函数错误的模糊调用。如果我用强类型仿函数签名替换 auto,它就能够正确区分这两个方法重载。
我注意到,当不使用 std::function 作为我的重载参数时,不会发生同样的问题。如果我的重载仅采用简单的 float 和 int,则即使使用 auto 关键字定义输入参数,编译器也可以正确区分这两个重载。我正在 VisualStudio 2012 中编译它。这可能只是 VS 编译器中的一个错误吗?我现在无法访问装有 GCC 或 Clang 的机器,但有人知道这是否可以在那里编译吗?
编译错误:对重载函数的调用不明确
class AmbiguousOverload
{
public:
static void OverloadedMethod(std::function<int()>) {}
static void OverloadedMethod(std::function<float()>) {}
};
int _tmain(int argc, _TCHAR* argv[])
{
auto func1 = []() -> float {
return 0.5f;
};
auto func2 = []() -> int {
return 12;
};
AmbiguousOverload::OverloadedMethod(func1);
AmbiguousOverload::OverloadedMethod(func2);
return 0;
}
编译
class AmbiguousOverload
{
public:
static void OverloadedMethod(std::function<int()>) {}
static void OverloadedMethod(std::function<float()>) {}
};
int _tmain(int argc, _TCHAR* argv[])
{
std::function<float()> func1 = []() -> float {
return 0.5f;
};
std::function<int()> func2 = []() -> int {
return 12;
};
AmbiguousOverload::OverloadedMethod(func1);
AmbiguousOverload::OverloadedMethod(func2);
return 0;
}
也可以编译
class AmbiguousOverload
{
public:
static void OverloadedMethod(int) {}
static void OverloadedMethod(float) {}
};
int _tmain(int argc, _TCHAR* argv[])
{
auto v1 = 0.5f;
auto v2 = 12;
AmbiguousOverload::OverloadedMethod(v1);
AmbiguousOverload::OverloadedMethod(v2);
return 0;
}
std::function
有一个贪婪的 template
构造函数,它将尝试从您传递给它的任何内容中构造它1。 std::function
不太适合在重载决策中使用。 它在一种情况下起作用,因为完美匹配优于转换,但正如所指出的,它很脆弱。
请注意,lambda 和
std::function
对象是不相关的类型。 std::function
知道如何包装 lambda,但它可以包装任何可复制可调用对象。 Lambda 是自动创建的可复制和可调用的匿名命名类。 std::function
是一个设计用于类型擦除调用的类。
想象一下,如果您的覆盖采取了
short
和 long
。 auto x = 2.0
和 short s = 2.0
将对应于 auto x = lambda
和 std::function<blah> f = lambda
情况。 当您显式选择类型时,您会导致类型转换,并且您显式选择的类型没有歧义。 但是当你这样做时 auto
它采用的是真实类型——而真实类型是不明确的。
SFINAE 或使用
std::result_of
进行标签调度可以让您手动处理这些覆盖。
在 c++14 中,这发生了一些变化。 现在构造函数只尝试吞咽兼容参数。 但是返回
int
的函数和返回 float
的函数都是相互兼容的,所以它对您的具体情况没有帮助。
1 在某种程度上这是
std::function
的缺陷。 它的“通用”构造函数实际上应该只在传入的类型既可复制且 std::result_of_t< X( Args... ) >
可以转换为 std::function
的结果类型时才参与重载决策。 我怀疑后概念这将被添加到标准中,因为后概念这确实很容易编写和表达(并且很少有符合标准的代码会被它破坏)。 然而,在这种特殊情况下,这实际上并没有帮助,因为 int
和 float
可以相互转换,并且没有办法说“这个构造函数可以工作,但实际上它不是首选选项” ”.