【问题标题】:While doing copy-elision, the compiler doesn't consider the copy constructor in overload resolution, when the move constructor is deleted. Why?在进行复制省略时,当删除移动构造函数时,编译器不会在重载决议中考虑复制构造函数。为什么?
【发布时间】:2015-08-02 19:42:26
【问题描述】:

我可以理解编译器在下面的代码中执行复制省略,因为在main() 中完成的所谓的copy-initialization 中没有调用复制和移动构造函数。见live example

#include <iostream>
struct S {
    S() = default;
    S(const S&) { std::cout << "copy ctor" << '\n'; }
    S(S&&) { std::cout << "move ctor" << '\n'; }
};

int main() {
    S s = S(); 
}

但我不明白为什么当我删除移动构造函数时代码无法编译,如下所示:

#include <iostream>
struct S {
    S() = default;
    S(const S&) { std::cout << "copy ctor" << '\n'; }
    S(S&&) = delete;
};

int main() {
    S s = S(); 
}

在这种情况下,我在 §12.8/32 (N4140) 中找不到任何可能禁止使用或省略 复制构造函数 的内容。这是第 12.8/32 节中引起我注意的一句话,这似乎表明应该在重载决议中考虑复制构造函数:

如果第一个重载决议失败或没有执行,或者如果 所选构造函数的第一个参数的类型不是 对对象类型的右值引用(可能是 cv 限定的), 再次执行重载决议,将对象视为 左值。

编辑

从下面T.C. 的一个 cmets 中,我了解到,当要复制的对象由右值指定时,编译器根据 §12.8/32,不会将复制构造函数视为候选对象对于副本,即使副本无论如何都会被省略。也就是说,最终结果将是使用默认构造函数构造对象s。相反,在这种情况下,标准要求(在哪里??)代码格式错误。除非我对这个方案的理解完全错误,否则这对我来说没有任何意义。

【问题讨论】:

  • 重载解析在找到删除的函数时不会失败。被删除的函数不是不存在,它们在某种意义上没有被删除。他们只是非法调用(他们可以被调用,如果你这样做,那就是一个错误)。
  • 在任何情况下,从 12.8/32 引用的内容都不适用。
  • return 语句之外,该段落仅适用于“要复制的对象由左值指定”等情况。 S() 不是左值。
  • 为什么要避免复制省略?
  • @François-MarieArouet 因为=delete 并不意味着“这个函数不存在”。如果您不希望函数存在-> 只是不要声明它。也就是说,=delete 表示如果尝试使用这种已删除的函数,编译器会报错。

标签: c++ language-lawyer c++14 copy-elision


【解决方案1】:

这与复制省略或构造函数无关;这只是重载决议。

如果我们有一对重载:

void f( T&& rv );
void f( const T& lv );

然后重载解析规则说f( T{} ) 更适合f(T&amp;&amp;)

复制省略可以省略复制或移动,但前提是代码定义明确(即使编译器选择不实现复制省略)。您的代码定义不明确,因为它指定调用已删除的函数。

【讨论】:

  • 但是为什么deleted函数必须参与重载解析呢?
  • @François-MarieArouet 就是这样。在这种情况下似乎更灵活:您可以选择将移动构造函数定义为已删除,或者在这种情况下省略它,它不存在并且将调用复制构造函数。
猜你喜欢
  • 1970-01-01
  • 2017-07-20
  • 2023-03-03
  • 2014-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-30
  • 1970-01-01
相关资源
最近更新 更多