英文:
Type deduction resullts in ambiguous call of overloaded function
问题
在混合类型推断和重载的情况下,您遇到了对于 Lambda 函数类型推断的一种行为,这一行为可能难以理解。
当编译这个程序时:
#include <functional>
#include <cstdlib>
int case_0(int const& x) {
return 2*x;
}
int case_1(int& x) {
x += 2;
return 2*x;
}
class Test {
public:
Test(int const n) : n(n) {}
int apply_and_increment(std::function<int(int const&)> f) {
n++;
return f(n);
}
int apply_and_increment(std::function<int(int&)> f) {
return f(n);
}
private:
int n;
};
int main() {
Test t(1);
auto f = [](int const& x) -> int {
return 3*x;
};
t.apply_and_increment(case_0); // 编译失败
t.apply_and_increment(std::function<int(int const&)>(case_0)); // 编译成功
t.apply_and_increment(case_1); // 编译成功
t.apply_and_increment(f); // 编译失败
return EXIT_SUCCESS;
}
编译的输出是:
$ g++ -std=c++20 different_coonstness.cpp -o test
different_coonstness.cpp: In function ‘int main()’:
different_coonstness.cpp:34:30: error: call of overloaded ‘apply_and_increment(int (&)(const int&))’ is ambiguous
34 | t.apply_and_increment(case_0);
| ^
different_coonstness.cpp:16:7: note: candidate: ‘int Test::apply_and_increment(std::function<int(const int&)>)’
16 | int apply_and_increment(std::function<int(int const&)> f) {
| ^~~~~~~~~~~~~~~~~~~
different_coonstness.cpp:20:7: note: candidate: ‘int Test::apply_and_increment(std::function<int(int&)>)’
20 | int apply_and_increment(std::function<int(int&)> f) {
| ^~~~~~~~~~~~~~~~~~~
different_coonstness.cpp:37:25: error: call of overloaded ‘apply_and_increment(main()::<lambda(const int&)>&)’ is ambiguous
37 | t.apply_and_increment(f);
| ^
different_coonstness.cpp:16:7: note: candidate: ‘int Test::apply_and_increment(std::function<int(const int&)>)’
16 | int apply_and_increment(std::function<int(int const&)> f) {
| ^~~~~~~~~~~~~~~~~~~
different_coonstness.cpp:20:7: note: candidate: ‘int Test::apply_and_increment(std::function<int(int&)>)’
20 | int apply_and_increment(std::function<int(int&)> f) {
| ^~~~~~~~~~~~~~~~~~~
就我所了解:
-
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:
#include <functional>
#include <cstdlib>
int case_0(int const& x) {
return 2*x;
}
int case_1(int& x) {
x += 2;
return 2*x;
}
class Test {
public:
Test(int const n) : n(n) {}
int apply_and_increment(std::function<int(int const&)> f) {
n++;
return f(n);
}
int apply_and_increment(std::function<int(int&)> f) {
return f(n);
}
private:
int n;
};
int main() {
Test t(1);
auto f = [](int const& x) -> int {
return 3*x;
};
t.apply_and_increment(case_0); // Fails compilation
t.apply_and_increment(std::function<int(int const&)>(case_0)); // Succeeds
t.apply_and_increment(case_1); // Succeeds
t.apply_and_increment(f); // Fails compilation
return EXIT_SUCCESS;
}
The output of the compilation is:
$ g++ -std=c++20 different_coonstness.cpp -o test
different_coonstness.cpp: In function ‘int main()’:
different_coonstness.cpp:34:30: error: call of overloaded ‘apply_and_increment(int (&)(const int&))’ is ambiguous
34 | t.apply_and_increment(case_0);
| ^
different_coonstness.cpp:16:7: note: candidate: ‘int Test::apply_and_increment(std::function<int(const int&)>)’
16 | int apply_and_increment(std::function<int(int const&)> f) {
| ^~~~~~~~~~~~~~~~~~~
different_coonstness.cpp:20:7: note: candidate: ‘int Test::apply_and_increment(std::function<int(int&)>)’
20 | int apply_and_increment(std::function<int(int&)> f) {
| ^~~~~~~~~~~~~~~~~~~
different_coonstness.cpp:37:25: error: call of overloaded ‘apply_and_increment(main()::<lambda(const int&)>&)’ is ambiguous
37 | t.apply_and_increment(f);
| ^
different_coonstness.cpp:16:7: note: candidate: ‘int Test::apply_and_increment(std::function<int(const int&)>)’
16 | int apply_and_increment(std::function<int(int const&)> f) {
| ^~~~~~~~~~~~~~~~~~~
different_coonstness.cpp:20:7: note: candidate: ‘int Test::apply_and_increment(std::function<int(int&)>)’
20 | int apply_and_increment(std::function<int(int&)> f) {
| ^~~~~~~~~~~~~~~~~~~
As far as I understand:
-
case_0
is ambiguous because there are 2 valid type conversions,std::function<int(const int&)>
andstd::function<int(int&)>
, and both overloaded functionsapply_and_increment()
can be applied. This is why the explicit type conversionstd::function<int(int const&)>(case_0)
is required. -
in
case_1
, the only valid conversion isstd::function<int(int&)>
, 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& x) -> int
, in the lambda function.
Why is not f
of type std::function<int(int const&)>
?
答案1
得分: 1
你对case_0
和case_1
的重载解析理解是正确的:
-
可以将一个非const引用分配给一个const引用,因此
case_0()
可以从两种使用的std::function
类型中调用,因此在使用隐式转换时,需要明确指定所需的std::function
类型,因此重载解析是模糊的。 -
不能将const引用分配给非const引用,因此
case_1()
无法从std::function<int(int const&)>
调用,只能从std::function<int(int&)>
调用,因此在使用隐式转换时,重载解析不是模糊的。
一个独立的函数本身不是std::function
对象,但可以被分配给兼容的std::function
对象。
同样,lambda本身不是std::function
对象,它是编译器定义的仿函数类型的实例,可以分配给兼容的std::function
对象。
在这两种情况下,std::function
充当代理,将其自己的参数传递给函数/lambda的参数,然后返回函数/lambda返回的任何内容。
因此,case_0
和f
的重载解析之所以失败,原因完全相同。当编译器必须隐式将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 thestd::function
types being used, thus overload resolution is ambiguous when an implicit conversion is used, requiring you to specify the desiredstd::function
type explicitly. -
A reference-to-const cannot be assigned to a reference-to-non-const, hence
case_1()
is not callable fromstd::function<int(int const&)>
, only fromstd::function<int(int&)>
, 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论