参数类型的函数是否具有选择了非常量参考的复制构造函数?

时间:2011-01-16 09:05:54

标签: c++ rvalue overload-resolution const-reference

前一段时间,当我想写一个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)'

你能解释为什么一个有效但另一个无效吗?

2 个答案:

答案 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. - 结束示例] 当参数具有类类型且参数表达式具有相同类型时,隐式   转换序列是一种身份转换。 [...]