参数类型的函数具有选择的非常量引用的复制构造函数?
不久前,当我想编写 is_callable
特征时,我对某些代码的以下行为感到困惑。重载解析不会调用接受非常量引用参数的函数,对吧?为什么它在下面不拒绝,因为构造函数需要一个 Test&
?我预计它需要 f(int)
!
struct Test {
Test() { }
// I want Test not be copyable from rvalues!
Test(Test&) { }
// But it's convertible to int
operator int() { return 0; }
};
void f(int) { }
void f(Test) { }
struct WorksFine { };
struct Slurper { Slurper(WorksFine&) { } };
struct Eater { Eater(WorksFine) { } };
void g(Slurper) { }
void g(Eater) { } // chooses this, as expected
int main() {
// Error, why?
f(Test());
// But this works, why?
g(WorksFine());
}
错误消息是
m.cpp: In function 'int main()':
m.cpp:33:11: error: no matching function for call to 'Test::Test(Test)'
m.cpp:5:3: note: candidates are: Test::Test(Test&)
m.cpp:2:3: note: Test::Test()
m.cpp:33:11: error: initializing argument 1 of 'void f(Test)'
您能解释一下为什么一个有效但另一个无效吗?
Some time ago I was confused by the following behavior of some code when I wanted to write a is_callable<F, Args...>
trait. Overload resolution won't call functions accepting arguments by non-const ref, right? Why doesn't it reject in the following because the constructor wants a Test&
? I expected it to take f(int)
!
struct Test {
Test() { }
// I want Test not be copyable from rvalues!
Test(Test&) { }
// But it's convertible to int
operator int() { return 0; }
};
void f(int) { }
void f(Test) { }
struct WorksFine { };
struct Slurper { Slurper(WorksFine&) { } };
struct Eater { Eater(WorksFine) { } };
void g(Slurper) { }
void g(Eater) { } // chooses this, as expected
int main() {
// Error, why?
f(Test());
// But this works, why?
g(WorksFine());
}
Error message is
m.cpp: In function 'int main()':
m.cpp:33:11: error: no matching function for call to 'Test::Test(Test)'
m.cpp:5:3: note: candidates are: Test::Test(Test&)
m.cpp:2:3: note: Test::Test()
m.cpp:33:11: error: initializing argument 1 of 'void f(Test)'
Can you please explain why one works but the other doesn't?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
重载解析选择与所提供的参数最匹配的函数。您提供了一个测试。无需转换——使用身份转换。因此函数解析选择f(Test)。无法从您提供的右值复制测试,但重载解析已成功...从不检查到 int 的转换。
选择 g(Eater) 是因为类型不完全匹配,不使用恒等转换,并且编译器必须找到有效的转换例程。
g(Slurper)
不会,因为您无法从提供的参数中得出一个结果。“为什么这个没有失败:
struct A { operator int(); }; void f(A&); void f(int); void g() { f(A()); }"
因为 f(A&) 对于所提供的参数来说不是可行的重载。在这种情况下,参数是一个引用,并且临时值不绑定到非常量这一事实可以影响解析。在这种情况下,它确实如此,并且该函数的该版本成为非候选版本,只留下一个可以运行的版本。
Overload resolution picks the function that is the closest match to the supplied argument. You supplied a Test. No conversion necessary -- identity conversion used. Thus function resolution chooses f(Test). Test can't be copied from rvalue, which you supplied, but overload resolution has succeeded already...conversion to int is never checked.
g(Eater)
is chosen because the types don't exactly match, the identity conversion is NOT used, and the compiler has to find a conversion routine that works.g(Slurper)
doesn't because you can't make one out of the supplied argument."Why doesn't this one fail:
struct A { operator int(); }; void f(A&); void f(int); void g() { f(A()); }
"Because f(A&) is not a viable overload for the supplied argument. In this case the parameter is a reference and the fact that temps don't bind to non-const is allowed to effect the resolution. In this case it does and the that version of the function becomes a non-candidate, leaving only the one and it works.
基本上,对于重载决策,假设类型 A 的对象可以转换为类型 A 的对象,而不管两者中任何一个的 cv 限定如何。
来自草案 n1905:
13.3.3.1:重载。重载解决方案。最佳可行函数。隐式转换序列
Basically, for overload resolution, it is assumed that an object of type A can be converted to an object of type A regardless of any cv-qualification on either of the two.
From draft n1905:
13.3.3.1: Overloading.Overload Resolution.Best Viable Function.Implicit Conversion Sequences