我想知道是否有人知道为什么下面的示例没有编译给出过载函数错误的模糊调用。如果我用强类型仿函数签名替换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;
}
答案 0 :(得分:9)
std::function
有一个贪婪的template
构造函数,它将尝试从传递它的所有内容构建它 1 。 std::function
不适合用于重载解析。它在一个案例中起作用,因为完美匹配比转换更受欢迎,但如上所述它很脆弱。
请注意,lambdas和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
可以相互转换,并且没有办法说“这个构造函数可以工作,但实际上它不是'一个首选的选项“。