std::function<...> 上的重载

发布于 2024-10-13 08:14:30 字数 2498 浏览 4 评论 0 原文

给出以下代码:-

#include <algorithm>
#include <iostream>
#include <functional>
#include <string>

void func(std::function<void(void)> param)
{
    param();
}

void func(std::function<void(int)> param)
{
    param(5);
}

int main(int argc, char* argv[])
{
    func([] () { std::cout << "void(void)" << std::endl; });
    func([] (int i) { std::cout << "void(int): " << i << std::endl; });

    std::string line;
    std::getline(std::cin, line);
    return 0;
}

VS2010 编译错误:-

CppTest.cpp(18): error C2668: 'func' : ambiguous call to overloaded function
1>          CppTest.cpp(11): could be 'void func(std::tr1::function<_Fty>)'
1>          with
1>          [
1>              _Fty=void (int)
1>          ]
1>          CppTest.cpp(6): or       'void func(std::tr1::function<_Fty>)'
1>          with
1>          [
1>              _Fty=void (void)
1>          ]
1>          while trying to match the argument list '(`anonymous-namespace'::<lambda0>)'
1>CppTest.cpp(19): error C2668: 'func' : ambiguous call to overloaded function
1>          CppTest.cpp(11): could be 'void func(std::tr1::function<_Fty>)'
1>          with
1>          [
1>              _Fty=void (int)
1>          ]
1>          CppTest.cpp(6): or       'void func(std::tr1::function<_Fty>)'
1>          with
1>          [
1>              _Fty=void (void)
1>          ]
1>          while trying to match the argument list '(`anonymous-namespace'::<lambda1>)'

g++-4.5 编译错误

program2.cpp: In function ‘int main(int, char**)’:
program2.cpp:18:68: error: call of overloaded ‘func(main(int, char**)::<lambda()>)’ is ambiguous
program2.cpp:6:10: note: candidates are: void func(std::function<void()>)
program2.cpp:11:10: note:                 void func(std::function<void(int)>)
program2.cpp:19:79: error: call of overloaded ‘func(main(int, char**)::<lambda(int)>)’ is ambiguous
program2.cpp:6:10: note: candidates are: void func(std::function<void()>)
program2.cpp:11:10: note:                 void func(std::function<void(int)>)

所以看来编译器无法弄清楚 lambda [] () -> void 只能赋值给 std::function,而 lambda[] (int) -> 则只能赋值给 lambda[] (int) -> 。 void 只能分配给 std::function。这是应该发生的还是只是编译器的缺陷?

Given the following code :-

#include <algorithm>
#include <iostream>
#include <functional>
#include <string>

void func(std::function<void(void)> param)
{
    param();
}

void func(std::function<void(int)> param)
{
    param(5);
}

int main(int argc, char* argv[])
{
    func([] () { std::cout << "void(void)" << std::endl; });
    func([] (int i) { std::cout << "void(int): " << i << std::endl; });

    std::string line;
    std::getline(std::cin, line);
    return 0;
}

Compile error from VS2010 :-

CppTest.cpp(18): error C2668: 'func' : ambiguous call to overloaded function
1>          CppTest.cpp(11): could be 'void func(std::tr1::function<_Fty>)'
1>          with
1>          [
1>              _Fty=void (int)
1>          ]
1>          CppTest.cpp(6): or       'void func(std::tr1::function<_Fty>)'
1>          with
1>          [
1>              _Fty=void (void)
1>          ]
1>          while trying to match the argument list '(`anonymous-namespace'::<lambda0>)'
1>CppTest.cpp(19): error C2668: 'func' : ambiguous call to overloaded function
1>          CppTest.cpp(11): could be 'void func(std::tr1::function<_Fty>)'
1>          with
1>          [
1>              _Fty=void (int)
1>          ]
1>          CppTest.cpp(6): or       'void func(std::tr1::function<_Fty>)'
1>          with
1>          [
1>              _Fty=void (void)
1>          ]
1>          while trying to match the argument list '(`anonymous-namespace'::<lambda1>)'

Compile error from g++-4.5

program2.cpp: In function ‘int main(int, char**)’:
program2.cpp:18:68: error: call of overloaded ‘func(main(int, char**)::<lambda()>)’ is ambiguous
program2.cpp:6:10: note: candidates are: void func(std::function<void()>)
program2.cpp:11:10: note:                 void func(std::function<void(int)>)
program2.cpp:19:79: error: call of overloaded ‘func(main(int, char**)::<lambda(int)>)’ is ambiguous
program2.cpp:6:10: note: candidates are: void func(std::function<void()>)
program2.cpp:11:10: note:                 void func(std::function<void(int)>)

So it seems the compiler can't figure out that a lambda [] () -> void can only be assigned to a std::function<void(void)>, and a lambda [] (int) -> void can only be assigned to a std::function<void(int)>. Is this supposed to happen or just a deficiency in the compilers?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

倥絔 2024-10-20 08:14:30

这是应该发生的还是只是编译器的缺陷?

这是应该发生的。 std::function 有一个构造函数模板,可以接受任何类型的参数。编译器直到选择并实例化构造函数模板之后才能知道它将遇到错误,并且它必须能够选择函数的重载才能执行此操作。

最直接的修复方法是使用强制转换或显式构造正确类型的 std::function 对象:

func(std::function<void()>([](){}));
func(std::function<void(int)>([](int){}));

如果您有一个支持 captureless-lambda-to-function-pointer 转换的编译器,并且你的 lambda 没有捕获任何内容,你可以使用原始函数指针:(

void func(void (*param)()) { }
void func(void (*param)(int)) { }

看起来你正在使用 Visual C++ 2010,它不支持此转换。直到 Visual Studio 2010 发布之前,该转换才添加到规范中晚了添加它。)


为了更详细地解释这个问题,请考虑以下内容:

template <typename T>
struct function {

    template <typename U>
    function(U f) { }
};

这基本上就是有问题的 std::function 构造函数的样子:您可以使用任何参数调用它,即使该参数没有意义并且会在其他地方导致错误。例如,function f(42); 将使用 U = int 调用此构造函数模板。

在您的具体示例中,编译器在重载解析期间找到两个候选函数:

void func(std::function<void(void)>)
void func(std::function<void(int)>)

参数类型(我们将其称为 F 的一些无法说出的 lambda 类型名称)与其中任何一个都不完全匹配,因此编译器开始查看它可以对 F 进行哪些转换,以尝试使其与这些候选函数之一匹配。当寻找转换时,它会找到前面提到的构造函数模板。

此时编译器看到的只是它可以调用任一函数,因为

  • 它可以使用其转换构造函数将 F 转换为 std::function U = F 并且
  • 它可以使用带有 U 的转换构造函数将 F 转换为 std::function = F。

在您的示例中,显然只有其中之一会成功且不会出现错误,但在一般情况下并非如此。编译器不能做任何进一步的事情。它必须报告歧义并失败。它无法选择其中一个,因为两种转换都同样好,并且两种重载都不比另一种更好。

Is this supposed to happen or just a deficiency in the compilers?

This is supposed to happen. std::function has a constructor template that can take an argument of any type. The compiler can't know until after a constructor template is selected and instantiated that it's going to run into errors, and it has to be able to select an overload of your function before it can do that.

The most straightforward fix is to use a cast or to explicitly construct a std::function object of the correct type:

func(std::function<void()>([](){}));
func(std::function<void(int)>([](int){}));

If you have a compiler that supports the captureless-lambda-to-function-pointer conversion and your lambda doesn't capture anything, you can use raw function pointers:

void func(void (*param)()) { }
void func(void (*param)(int)) { }

(It looks like you are using Visual C++ 2010, which does not support this conversion. The conversion was not added to the specification until just before Visual Studio 2010 shipped, too late to add it in.)


To explain the problem in a bit more detail, consider the following:

template <typename T>
struct function {

    template <typename U>
    function(U f) { }
};

This is basically what the std::function constructor in question looks like: You can call it with any argument, even if the argument doesn't make sense and would cause an error somewhere else. For example, function<int()> f(42); would invoke this constructor template with U = int.

In your specific example, the compiler finds two candidate functions during overload resolution:

void func(std::function<void(void)>)
void func(std::function<void(int)>)

The argument type, some unutterable lambda type name that we will refer to as F, doesn't match either of these exactly, so the compiler starts looking at what conversions it can do to F to try and make it match one of these candidate functions. When looking for conversions, it finds the aforementioned constructor template.

All the compiler sees at this point is that it can call either function because

  • it can convert F to std::function<void(void)> using its converting constructor with U = F and
  • it can convert F to std::function<void(int)> using its converting constructor with U = F.

In your example it is obvious that only one of these will succeed without error, but in the general case that isn't true. The compiler can't do anything further. It has to report the ambiguity and fail. It can't pick one because both conversions are equally good and neither overload is better than the other.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文