Type deduction results in ambiguous call of overloaded function.

huangapple go评论102阅读模式
英文:

Type deduction resullts in ambiguous call of overloaded function

问题

在混合类型推断和重载的情况下,您遇到了对于 Lambda 函数类型推断的一种行为,这一行为可能难以理解。

当编译这个程序时:

  1. #include <functional>
  2. #include <cstdlib>
  3. int case_0(int const& x) {
  4. return 2*x;
  5. }
  6. int case_1(int& x) {
  7. x += 2;
  8. return 2*x;
  9. }
  10. class Test {
  11. public:
  12. Test(int const n) : n(n) {}
  13. int apply_and_increment(std::function<int(int const&)> f) {
  14. n++;
  15. return f(n);
  16. }
  17. int apply_and_increment(std::function<int(int&)> f) {
  18. return f(n);
  19. }
  20. private:
  21. int n;
  22. };
  23. int main() {
  24. Test t(1);
  25. auto f = [](int const& x) -> int {
  26. return 3*x;
  27. };
  28. t.apply_and_increment(case_0); // 编译失败
  29. t.apply_and_increment(std::function<int(int const&)>(case_0)); // 编译成功
  30. t.apply_and_increment(case_1); // 编译成功
  31. t.apply_and_increment(f); // 编译失败
  32. return EXIT_SUCCESS;
  33. }

编译的输出是:

  1. $ g++ -std=c++20 different_coonstness.cpp -o test
  2. different_coonstness.cpp: In function int main()’:
  3. different_coonstness.cpp:34:30: error: call of overloaded apply_and_increment(int (&)(const int&))’ is ambiguous
  4. 34 | t.apply_and_increment(case_0);
  5. | ^
  6. different_coonstness.cpp:16:7: note: candidate: int Test::apply_and_increment(std::function<int(const int&)>)’
  7. 16 | int apply_and_increment(std::function<int(int const&)> f) {
  8. | ^~~~~~~~~~~~~~~~~~~
  9. different_coonstness.cpp:20:7: note: candidate: int Test::apply_and_increment(std::function<int(int&)>)’
  10. 20 | int apply_and_increment(std::function<int(int&)> f) {
  11. | ^~~~~~~~~~~~~~~~~~~
  12. different_coonstness.cpp:37:25: error: call of overloaded apply_and_increment(main()::<lambda(const int&)>&)’ is ambiguous
  13. 37 | t.apply_and_increment(f);
  14. | ^
  15. different_coonstness.cpp:16:7: note: candidate: int Test::apply_and_increment(std::function<int(const int&)>)’
  16. 16 | int apply_and_increment(std::function<int(int const&)> f) {
  17. | ^~~~~~~~~~~~~~~~~~~
  18. different_coonstness.cpp:20:7: note: candidate: int Test::apply_and_increment(std::function<int(int&)>)’
  19. 20 | int apply_and_increment(std::function<int(int&)> f) {
  20. | ^~~~~~~~~~~~~~~~~~~

就我所了解:

  • case_0 是模棱两可的,因为存在两个有效的类型转换,std::function<int(const int&)>std::function<int(int&)>,并且都可以应用于两个重载的函数 apply_and_increment()。这就是为什么需要显式类型转换 std::function<int(int const&)>(case_0)

  • case_1 中,唯一有效的转换是 std::function<int(int&)>,因此没有歧义。

我不太熟悉类型推断和 Lambda,所以我对 t.apply_and_increment(f) 无法通过编译感到有些惊讶。我预期函数的类型会通过 Lambda 函数的类型签名 [](int const& x) -> int 来推断,为什么 f 的类型不是 std::function<int(int const&)> 呢?

(Note: The original code provided has some HTML character escapes in angle brackets ("<" and ">") that may not be valid in C++. I've kept the escapes in the translation for consistency, but you may need to adjust them in the actual code.)

英文:

While mixing type deduction with overloading, I stumbled upon a behavior of type deduction for lambda functions that I find difficult to understand.

When compiling this program:

  1. #include &lt;functional&gt;
  2. #include &lt;cstdlib&gt;
  3. int case_0(int const&amp; x) {
  4. return 2*x;
  5. }
  6. int case_1(int&amp; x) {
  7. x += 2;
  8. return 2*x;
  9. }
  10. class Test {
  11. public:
  12. Test(int const n) : n(n) {}
  13. int apply_and_increment(std::function&lt;int(int const&amp;)&gt; f) {
  14. n++;
  15. return f(n);
  16. }
  17. int apply_and_increment(std::function&lt;int(int&amp;)&gt; f) {
  18. return f(n);
  19. }
  20. private:
  21. int n;
  22. };
  23. int main() {
  24. Test t(1);
  25. auto f = [](int const&amp; x) -&gt; int {
  26. return 3*x;
  27. };
  28. t.apply_and_increment(case_0); // Fails compilation
  29. t.apply_and_increment(std::function&lt;int(int const&amp;)&gt;(case_0)); // Succeeds
  30. t.apply_and_increment(case_1); // Succeeds
  31. t.apply_and_increment(f); // Fails compilation
  32. return EXIT_SUCCESS;
  33. }

The output of the compilation is:

  1. $ g++ -std=c++20 different_coonstness.cpp -o test
  2. different_coonstness.cpp: In function int main()’:
  3. different_coonstness.cpp:34:30: error: call of overloaded apply_and_increment(int (&amp;)(const int&amp;))’ is ambiguous
  4. 34 | t.apply_and_increment(case_0);
  5. | ^
  6. different_coonstness.cpp:16:7: note: candidate: int Test::apply_and_increment(std::function&lt;int(const int&amp;)&gt;)’
  7. 16 | int apply_and_increment(std::function&lt;int(int const&amp;)&gt; f) {
  8. | ^~~~~~~~~~~~~~~~~~~
  9. different_coonstness.cpp:20:7: note: candidate: int Test::apply_and_increment(std::function&lt;int(int&amp;)&gt;)’
  10. 20 | int apply_and_increment(std::function&lt;int(int&amp;)&gt; f) {
  11. | ^~~~~~~~~~~~~~~~~~~
  12. different_coonstness.cpp:37:25: error: call of overloaded apply_and_increment(main()::&lt;lambda(const int&amp;)&gt;&amp;)’ is ambiguous
  13. 37 | t.apply_and_increment(f);
  14. | ^
  15. different_coonstness.cpp:16:7: note: candidate: int Test::apply_and_increment(std::function&lt;int(const int&amp;)&gt;)’
  16. 16 | int apply_and_increment(std::function&lt;int(int const&amp;)&gt; f) {
  17. | ^~~~~~~~~~~~~~~~~~~
  18. different_coonstness.cpp:20:7: note: candidate: int Test::apply_and_increment(std::function&lt;int(int&amp;)&gt;)’
  19. 20 | int apply_and_increment(std::function&lt;int(int&amp;)&gt; f) {
  20. | ^~~~~~~~~~~~~~~~~~~

As far as I understand:

  • case_0 is ambiguous because there are 2 valid type conversions, std::function&lt;int(const int&amp;)&gt; and std::function&lt;int(int&amp;)&gt;, and both overloaded functions apply_and_increment() can be applied. This is why the explicit type conversion std::function&lt;int(int const&amp;)&gt;(case_0) is required.

  • in case_1, the only valid conversion is std::function&lt;int(int&amp;)&gt;, so there is no ambiguity.

I am not very familiar with type deduction and lambdas, so I am a bit surprised that t.apply_and_increment(f) fails to compile. I would expect that the type of the function would be deduced by the type signature, [](int const&amp; x) -&gt; int, in the lambda function.

Why is not f of type std::function&lt;int(int const&amp;)&gt;?

答案1

得分: 1

你对case_0case_1的重载解析理解是正确的:

  • 可以将一个非const引用分配给一个const引用,因此case_0()可以从两种使用的std::function类型中调用,因此在使用隐式转换时,需要明确指定所需的std::function类型,因此重载解析是模糊的。

  • 不能将const引用分配给非const引用,因此case_1()无法从std::function&lt;int(int const&amp;)&gt;调用,只能从std::function&lt;int(int&amp;)&gt;调用,因此在使用隐式转换时,重载解析不是模糊的。

一个独立的函数本身不是std::function对象,但可以被分配给兼容的std::function对象。

同样,lambda本身不是std::function对象,它是编译器定义的仿函数类型的实例,可以分配给兼容的std::function对象。

在这两种情况下,std::function充当代理,将其自己的参数传递给函数/lambda的参数,然后返回函数/lambda返回的任何内容。

因此,case_0f的重载解析之所以失败,原因完全相同。当编译器必须隐式case_0/f转换为std::function对象时,转换是模糊的,因为case_0/f可以从使用的两种std::function类型中调用。

英文:

Your understanding of overload resolution for case_0 and case_1 is correct:

  • A reference-to-non-const can be assigned to a reference-to-const, hence case_0() is callable from both of the std::function types being used, thus overload resolution is ambiguous when an implicit conversion is used, requiring you to specify the desired std::function type explicitly.

  • A reference-to-const cannot be assigned to a reference-to-non-const, hence case_1() is not callable from std::function&lt;int(int const&amp;)&gt;, only from std::function&lt;int(int&amp;)&gt;, thus overload resolution is not ambiguous when an implicit conversion is used.

A standalone function is not itself a std::function object, but can be assigned to a compatible std::function object.

Likewise, a lambda is not itself a std::function object, it is an instance of a compiler-defined functor type, which can be assigned to a compatible std::function object.

In both cases, std::function acts as a proxy, passing its own parameters to the function/lambda's parameters, and then returning whatever the function/lambda returns.

So, overload resolution fails for both case_0 and f for the exact same reason. When the compiler has to implicitly convert case_0/f into a std::function object, the conversion is ambiguous because case_0/f is callable from both of the std::function types being used.

huangapple
  • 本文由 发表于 2023年2月18日 09:09:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/75490545.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定