前一段时间,当我想写一个is_callable<F, Args...>
特征时,我对某些代码的以下行为感到困惑。重载决议不会调用非const ref接受参数的函数,对吧?为什么不在下面拒绝因为构造函数需要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)'
你能解释为什么一个有效但另一个无效吗?
答案 0 :(得分:3)
重载分辨率选择与提供的参数最匹配的函数。你提供了一个测试。无需转换 - 使用身份转换。因此功能分辨率选择f(测试)。无法从您提供的rvalue复制测试,但重载解析已经成功...从不检查转换为int。
选择 g(Eater)
是因为类型不完全匹配,不使用标识转换,编译器必须找到有效的转换例程。 g(Slurper)
没有,因为你不能从提供的参数中做出一个。
“为什么这不会失败:struct A { operator int(); }; void f(A&); void f(int); void g() { f(A()); }
”
因为f(A&amp;)不是所提供参数的可行重载。在这种情况下,参数是一个引用,并且temps不绑定到非const的事实允许影响分辨率。在这种情况下确实如此,该函数的版本变为非候选版本,只留下一个版本并且它可以正常工作。
答案 1 :(得分:0)
基本上,对于重载解析,假设类型A的对象可以转换为类型A的对象,而不管两者中的任何一个都具有任何cv限定。
来自草案n1905:
13.3.3.1:Overloading.Overload Resolution.Best Viable Function.Implicit Conversion Sequenceces
6 当参数类型不是引用时,隐式转换序列模拟参数表达式中参数的复制初始化。隐式转换序列是转换参数所需的序列 表达式到参数类型的右值。 [注意:当参数具有类类型时,这是概念性的 为第13条的目的而定义的转换;实际初始化是根据构造函数定义的,而不是 转换。 - 结束注释] 顶层cv资格的任何差异都归入初始化本身并且 不构成转换。 [示例:类型A的参数可以从const A类型的参数初始化。 该情况的隐式转换序列是标识序列;它不包含从const A到的“转换” A. - 结束示例] 当参数具有类类型且参数表达式具有相同类型时,隐式 转换序列是一种身份转换。 [...]