【问题标题】:Function with parameter type that has a copy-constructor with non-const ref chosen?具有选择了非常量引用的复制构造函数的参数类型的函数?
【发布时间】:2011-01-16 09:05:54
【问题描述】:

前段时间,当我想编写is_callable<F, Args...> trait 时,我对某些代码的以下行为感到困惑。重载解析不会调用通过非常量 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)'

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

【问题讨论】:

  • 它在我的 MSVC++ 10 编译器上编译得很好。
  • 好吧...我明白了..“Fehler”的意思是“错误”..“Anmerkung”的意思是“注意”..哈哈..学习新的人类语言...这是什么语言方式? :|
  • @Nawaz 哎呀,我忘了翻译!
  • @Johannes : 呵呵..没问题...我只是用 gcc 编译的,我得到了翻译:P.. 还有 google...
  • 请注意,Comeau 失败并出现同样的错误。

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


【解决方案1】:

重载解析选择与提供的参数最接近的函数。你提供了一个测试。无需转换——使用身份转换。因此函数解析选择 f(Test)。无法从您提供的右值复制测试,但重载解析已经成功...从不检查转换为 int。

g(Eater) 被选中是因为类型不完全匹配,没有使用身份转换,编译器必须找到一个有效的转换例程。 g(Slurper) 没有,因为您无法从提供的参数中得出一个。

“为什么这个不失败:struct A { operator int(); }; void f(A&); void f(int); void g() { f(A()); }

因为 f(A&) 对于提供的参数来说不是可行的重载。在这种情况下,参数是一个引用,并且 temps 不绑定到非 const 的事实允许影响分辨率。在这种情况下,它确实如此,并且该函数的版本成为非候选者,只留下一个并且它可以工作。

【讨论】:

  • 为什么这个没有失败:struct A { operator int(); }; void f(A&); void f(int); void g() { f(A()); },为什么它对我的其他测试有效?为什么将WorksFine 转换为Eater 比转换为Slurper 更好?
  • @Noah:如果Test不能从右值复制,那我怎么能说重载解析成功了呢? :-/
  • 但是,如果参数具有引用类型,则转换顺序包括绑定引用。对非const 的引用不能绑定到右值这一事实应该影响候选函数是否实际可行。
  • @Nawaz - 我猜这有点不直观,但根据我的解释,因为 f 的可用版本之一的参数类型与提供给它的表达式的类型完全匹配,所以从来没有测试过完成以查看您是否可以实际调用该函数,构造所提供参数的副本等...我什至不认为引用规则适用(我相信由于“直接绑定”而最终会说同样的话stuff) 因为文本是在讨论参数的类型,而不是参数。
  • @Charles 实际上有一段明确说明了这件事,因此 clang/gcc/comeau 是正确的。但它有点隐藏!
【解决方案2】:

基本上,对于重载决议,假设 A 类型的对象可以转换为 A 类型的对象,而不管两者中的任何一个的任何 cv 限定。

来自草案 n1905:

13.3.3.1:重载.重载分辨率.最佳可行函数.隐式转换序列

6 当参数类型不是引用时,隐式转换序列模拟参数表达式中参数的复制初始化。隐式转换序列是转换参数所需的序列 表达式为参数类型的右值。 [ 注意:当参数有类类型时,这是一个概念 为第 13 条的目的而定义的转换;实际的初始化是根据构造函数定义的,而不是 转换。 — 结束说明 ] 顶级 cv 限定的任何差异都包含在初始化本身中,并且确实 不构成转换。 [示例:A 类型的参数可以从 const A 类型的参数初始化。 这种情况的隐式转换序列是恒等序列;它不包含从 const A 到 A. — 结束示例 ] 当参数具有类类型并且参数表达式具有相同类型时,隐式 转换序列是身份转换。 [...]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-24
    • 2010-09-07
    相关资源
    最近更新 更多