【问题标题】:Why does overload resolution prefer a move constructor over a non-special constructor when equally good conversion functions are available?当同样好的转换函数可用时,为什么重载决议更喜欢移动构造函数而不是非特殊构造函数?
【发布时间】:2021-02-21 22:31:09
【问题描述】:

以下程序:

#include <iostream>

struct A 
{ 
  A() { std::cout << "A()\n"; }
  A(int) { std::cout << "A(int)\n"; }
};

struct C {
  operator int() {
    std::cout << "operator int\n";
    return 42;
  }
  operator A() {
    std::cout << "operator A\n";
    return A();
  }
};

int main() {
    auto a = A{C{}};
};

编译并在运行时打印:

operator A
A()

Godbolt link

这表明选择A 的移动构造函数来执行初始化,并调用C::operator A()C 转换为移动构造函数期望的类型。

或者,可以选择A::A(int) 构造函数,并调用C::operator int()。但是A::A(int) 显然失去了重载决议。

为什么会这样?我无法在标准中看到任何解释为什么 A 的移动构造函数赢得重载决议的规则。

(这个问题的灵感来自this answer;我不明白为什么该答案中的代码有效。)

【问题讨论】:

  • 我认为因为通过 int 需要 2 次转换,但标准只允许 1 次
  • @AlanBirtles 我不认为这就是原因。只允许一种用户定义的转换将参数类型转换为参数类型。在这两种情况下,都有一个用户定义的转换,即转换运算符。重载解决过程最终选择的对A 的构造函数的调用不算在内。这发生在重载决议完成之后。
  • 由于复制省略,编译器似乎不会选择A(int),但我不确定该规则来自哪里。
  • 似乎强制复制省略导致移动构造函数成为首选。基于此gcc bug reportthis proposal,这似乎是意图,但标准对此并不十分清楚。该提案中的一些(但不是全部)措辞似乎已合并到工作草案中,因此当前的工作草案可能解决了歧义;我说不出来。
  • @cigien 我不明白该提案的相关性。似乎都是用移动替换副本,而不是完全省略移动。

标签: c++ c++17 overload-resolution


【解决方案1】:

似乎 GCC 和 Clang 都通过在初始化 cv2 T 类型的对象时直接考虑到 cv T 的转换函数作为候选者,对 CWG2327 实施了修复,除了构造函数。

C::operator A() 本身被视为候选时,它是完全匹配的,因此优于需要用户定义转换的构造函数调用。

该问题仍处于起草状态,因此您不会在措辞中找到这一点。

值得注意的是,两者都以-std=c++14 拒绝。

【讨论】:

    猜你喜欢
    • 2015-06-10
    • 1970-01-01
    • 1970-01-01
    • 2012-06-28
    • 1970-01-01
    • 2013-04-25
    • 2018-05-26
    • 2012-04-29
    相关资源
    最近更新 更多