为什么move构造函数既没有声明也没有用clang删除
Why is the move constructor neither declared nor deleted with clang?
考虑以下类。
struct with_copy {
with_copy() = default;
with_copy(with_copy const&) {}
with_copy& operator=(with_copy const&) { return *this; }
};
struct foo {
with_copy c;
std::unique_ptr<int> p;
};
with_copy
有复制构造函数吗?对它是明确定义的with_copy
有移动构造函数吗?否。显式复制构造函数阻止生成它with_copy
是否有已删除的移动构造函数?不。没有move构造函数和删除了一个构造函数是不一样的。已删除的移动构造函数将尝试移动格式不正确的构造函数,而不是退化为副本with_copy
可复制吗?对它的复制构造函数用于复制with_copy
可移动吗?对它的复制构造函数用于移动
现在是棘手的问题。
foo
有复制构造函数吗?对它有一个已删除的定义,因为它的默认定义会由于调用unique_ptr
的已删除副本构造函数而格式不正确foo
有移动构造函数吗?GCC同意,clang拒绝foo
是否有已删除的移动构造函数?GCC和clang都拒绝了foo
可复制吗?否。它的复制构造函数已被删除foo
可移动吗?GCC同意,clang拒绝
(当考虑分配而不是施工时,行为是相似的。)
在我看来,GCC是正确的。foo
应该有一个对每个成员执行移动的移动构造函数,在with_copy
的情况下,它会退化为一个副本。Clang的行为似乎很荒谬:我有一个有两个可移动构件的骨料,但我的骨料是一块不可移动的砖。
谁是对的?
C++11,或者更确切地说是n3485,[class.copy]/9:
如果类
X
的定义没有显式声明移动构造函数,则会隐式声明一个当且仅当
- CCD_ 15不具有用户声明的复制构造函数
- CCD_ 16不具有用户声明的拷贝分配运算符
- CCD_ 17不具有用户声明的移动分配运算符
X
没有用户声明的析构函数,并且- move构造函数不会被隐式定义为已删除
和/11:
隐式声明的复制/移动构造函数是其类的
inline public
成员。默认副本/如果X
具有,则类X
的移动构造函数被定义为已删除(8.4.3)
- […]
- 对于复制构造函数,是右值引用类型的非静态数据成员,或者
- 对于move构造函数,非静态数据成员或类型为没有move构造函数,并且不具有可复制性
由于with_copy
是不可复制的,foo
将具有无移动构造函数(它将被定义为已删除,因此不会隐式声明)。
C++1y,或者更确切地说,github repo从2013-11-12提交e31867c0;包含DR1402:
/9:
如果类
X
的定义没有显式声明移动构造函数,其中一个将被隐式声明为默认当且仅当如果
- CCD_ 25不具有用户声明的复制构造函数
- CCD_ 26不具有用户声明的拷贝分配运算符
X
没有用户声明的移动分配运算符,并且X
没有用户声明的析构函数
和/11:
隐式声明的复制/移动构造函数是
inline public
其班级的成员。类X
的默认复制/移动构造函数如果X
具有,则定义为已删除(8.4.3)
- […]
- 对于复制构造函数,是右值引用类型的非静态数据成员
被定义为已删除的默认移动构造函数被忽略过载分辨率(13.3,13.4)。
这里,foo
将有一个move构造函数。
我不太确定你测试了什么,但它foo
肯定既可以移动赋值,也可以移动构造。诚然,这并没有说明移动构造函数或移动赋值是可访问的,只是右值的构造或赋值有效。clang(clang版本3.5(trunk 196718))和gcc(gcc版本4.9.0 20131031(实验性)(gcc))都同意这一评估。这是我尝试过的完整来源:
#include <iostream>
#include <type_traits>
#include <memory>
struct with_copy {
with_copy() = default;
with_copy(with_copy const&) {}
with_copy& operator=(with_copy const&) { return *this; }
};
struct foo {
with_copy c;
std::unique_ptr<int> p;
};
int main()
{
std::cout << "move constructible: "
<< std::is_move_constructible<foo>::value << 'n';
std::cout << "move assignable: "
<< std::is_move_assignable<foo>::value << 'n';
foo f0;
foo f1 = std::move(f0);
f0 = std::move(f1);
}
- 奇怪的结构&GCC&clang(void*返回类型)
- 数据成员SFINAE的C++17测试:gcc vs clang
- 当我编译webrtc服务器时,Windows上只支持clang-cl
- 为什么在Windows上的VS 2019和Clang 9中"size_t"在没有标题的情况下工作
- 我可以将一个用clang c++11编译的对象与另一个用c++17编译的对象链接起来吗
- Clang bug?使用指针作为模板参数
- clang整洁10忽略了我的NOLINT命令
- 如何防止clang格式在流运算符调用之间添加换行符<<
- 为什么需要复制构造函数,在哪些情况下它们非常有用
- 在clang++预处理器中确定gcc工具链版本
- 为什么 Clang 不允许"and"作为函数名称?
- 带有 -stdlib=libc++ 的 clang++ 9.0.1 找不到<optional>
- clang格式:宏的缩进
- CLANG 编译器 说:变量"PTR"可能未初始化
- clang格式:禁用排序包含
- 为什么lambda在clang上崩溃而不是在gcc上崩溃
- gcc和clang在表达式是否为常量求值的问题上存在分歧
- 循环展开 - G++ 与 Clang++
- 如何使用Clang/GCC在Mac上为C/C++设置VSCode
- 为什么move构造函数既没有声明也没有用clang删除