【发布时间】:2021-05-29 22:22:20
【问题描述】:
在摆弄复制省略时,我遇到了这种奇怪的行为:
class Obj {
public:
Obj() = default;
Obj(Obj&&) = delete;
Obj(const Obj&) { std::cout << "Copy" << std::endl; }
};
Obj f1() {
Obj o;
return o; // error C2280: move constructor is deleted
}
Obj f2() {
Obj o;
return Obj(o); // this however works fine
}
int main() {
Obj p = f1();
Obj q = f2();
return 0;
}
GCC 和 Clang 接受此代码,并且能够在这两种情况下使用复制省略。
在f1() MSVC 抱怨它不能返回o,因为Obj 的移动构造函数被删除了。但是,我希望它能够依赖于复制构造函数。 这是 MSVC 中的错误还是这种期望的行为(我不明白)和 GCC / Clang 过于宽松?
如果我提供一个移动构造函数,MSVC 可以在编译为 Release 时省略移动。
有趣的是,MSVC 能够编译 f2()。据我了解,这是由于返回构造函数调用的结果时强制复制省略所致。但是,如果我手动复制它,我只能按值返回 o,这感觉违反直觉。
我知道这种情况可能与实际使用无关,因为可复制对象通常也是可移动的,但我对底层机制很感兴趣。
这里是在线测试示例:https://godbolt.org/z/sznds7
【问题讨论】:
-
在 f1 中,复制省略在 f2 中是可选的。 Msvc 是对的,但 gcc 和 clang 也是。见en.cppreference.com/w/cpp/language/copy_elision
-
虽然
deleted 函数确实参与重载决议,但似乎有一个exception 用于特殊成员函数,它们被显式忽略。在我看来,这就像一个 MSVC 错误。 -
@cigien - 这适用吗?此移动构造函数不是 默认 移动 (
= default) ... -
@cigien - 但在该部分给出的示例中,正在讨论的移动构造函数,对于
struct B,是= default,但它被“定义”删除,因为struct B包含一个成员是一个结构,它有一个已删除的 (= delete) 移动构造函数 ... -
@cigien 当一个特殊的成员函数被声明为默认值时,编译器会提供一个遵循一组规则的隐式定义。有时,这些规则说应该删除该函数(例如,具有不可移动成员的类的移动构造函数);然后该功能被“定义为已删除”。在这种情况下,重载决议的行为就像根本没有声明函数一样。
标签: c++ visual-c++ c++17 language-lawyer copy-elision