c++ 11 auto, std::function和对重载函数的二义调用

C++11 auto, std::function and ambiguous call to overloaded function

本文关键字:函数 重载 调用 auto std function c++      更新时间:2023-10-16

我想知道是否有人知道为什么下面的示例不编译给出一个模棱两可的调用重载函数错误。如果我用强类型函子签名替换auto,它就能正确区分这两种方法重载。

我注意到当不使用std::function作为我的重载参数时不会发生同样的问题。如果我的重载只接受一个简单的float和int,即使使用auto关键字定义输入参数,编译器也可以正确地区分这两种重载。我在VisualStudio 2012中编译这个。这可能只是一个bug在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构造函数,它将尝试从你传递给它的任何东西构造它1std::function不太适合用于过载分辨率。它在一种情况下是有效的,因为完美匹配比转换更受欢迎,但正如前面提到的,它是脆弱的。

注意lambda和std::function对象是不相关的类型。std::function知道如何包装lambda,但它可以包装任何可复制的可调用对象。lambda是自动创建的匿名类,可复制和可调用。std::function是一个用于类型擦除调用的类。

想象一下,如果您的覆盖使用shortlongauto x = 2.0short s = 2.0对应auto x = lambdastd::function<blah> f = lambda。当您显式地选择类型时,会导致类型转换,并且您显式地选择的类型没有歧义。但是当你做auto的时候,它取的是真实的类型——而真实的类型是有歧义的。

SFINAE或使用std::result_of的标签调度将允许您手动处理这些覆盖。

在c++14中有所改变。现在构造函数只尝试吞下与兼容的参数。但是一个返回int的函数和一个返回float的函数都是相互兼容的,所以它对你的具体情况没有帮助。


1在一定程度上这是std::function的一个缺陷。它的"通用"构造函数实际上应该只在传入的类型 std::result_of_t< X( Args... ) >都可以转换为std::function的结果类型时才参与重载解析。我怀疑后概念将被添加到标准中,因为后概念非常容易编写和表达(并且很少有符合标准的代码会被它破坏)。然而,在这种特殊情况下,这实际上没有帮助,因为intfloat可以相互转换,并且没有办法说"这个构造函数将工作,但实际上它不是首选选项"。