我有以下代码:
#include <cstdio>
#include <iostream>
using std::cout;
struct SomeType {
SomeType() {}
SomeType(const SomeType &&other) {
cout << "SomeType(SomeType&&)\n";
*this = std::move(other);
}
void operator=(const SomeType &) {
cout << "operator=(const SomeType&)\n";
}
void operator=(SomeType &&) {
cout << "operator=(SomeType&&)\n";
}
};
int main() {
SomeType a;
SomeType b(std::move(a));
b = std::move(a);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我希望移动构造函数调用移动赋值运算符.这是该程序的输出:
SomeType(SomeType&&)
operator=(const SomeType&)
operator=(SomeType&&)
Run Code Online (Sandbox Code Playgroud)
如您所见,移动赋值运算符已成功调用,但在分配给*this内部移动构造函数时则不会.为什么会这样,我能以某种方式修复它吗?
std::move()如果在未定义移动构造函数的用户定义对象上调用会发生什么?是简单复制的吗?
我想让一些类使用自动生成的构造函数,但是不可复制(但仍可移动).目前我这样做:
class A
{
public:
A() = default;
A(const A&) = delete;
A(A&&) = default;
A& operator=(const A&) = delete;
A& operator=(A&&) = default;
}
Run Code Online (Sandbox Code Playgroud)
我想知道是否真的有必要这么明确.如果我这样写的话怎么办:
class A
{
A(const A&) = delete;
A& operator=(const A&) = delete;
}
Run Code Online (Sandbox Code Playgroud)
它仍然可以工作吗?对于其他情况,最小的默认值和删除集是什么 - 不可复制的非可移动类和具有虚拟析构函数的类?
是否有任何测试代码可用于快速查看隐式创建的构造函数?
下面的代码在Visual Studio 2013下会崩溃
我想知道为什么:在这种情况下编写移动构造函数的正确方法是什么?删除移动构造函数可以解决问题。这是 VC++ 的错误还是这段代码有错误?
移动构造函数的默认定义有什么不同,可以使此代码不会崩溃,而我自己的定义却会崩溃?
#include <memory>
#include <vector>
class A
{};
class Foo
{
public:
Foo(std::unique_ptr<A> ref) : mRef(std::move(ref)) {}
Foo(Foo&& other) : mRef(std::move(other.mRef)) {}
Foo(const Foo& other) {}
Foo& operator=(const Foo& other) { return *this; }
protected:
std::unique_ptr<A> mRef;
};
int main(int argc, char *argv[])
{
std::vector<Foo>({ Foo(std::make_unique<A>()), Foo(std::make_unique<A>()) });
// Crash : Debug Assertion Failed !
// Expression : _BLOCK_TYPE_IS_VALID(pHead->nBlockUse)
}
Run Code Online (Sandbox Code Playgroud)
可能与此有某种关系,对吧?
与 2013 年相比,initializer_list 中的双重删除
这是带有充实的复制构造函数和赋值的实际代码,但错误是完全相同的
class A
{
public:
std::unique_ptr<A> clone() { return …Run Code Online (Sandbox Code Playgroud) 我这里有两段代码给你看.它们是两个类,每个类都提供一个Move Constructor和一个返回临时函数的函数.
我很困惑:在这两种情况下,我都定义了一个Move Constructor和一个返回临时的随机成员函数.但行为改变了,我的问题就是原因.
请注意,在以下示例中,运算符<<被重载以便打印列表(在第一种情况下)和双数据成员(在第二种情况下).
移动构造者得到了认可
template<typename T>
class GList
{
public:
GList() : il{ nullptr } {}
GList(const T& val) : il{ new Link<T>{ val,nullptr } } {}
GList(const GList<T>& copy) {}
GList(GList<T>&& move)
{
std::cout << "[List] Move constructor called" << std::endl;
// ... code ...
}
// HERE IS THE FUNCTION WHICH RETURNS A TEMPORARY!
GList<T> Reverse()
{
GList<T> result;
if (result.il == nullptr)
return *this;
...
...
...
return …Run Code Online (Sandbox Code Playgroud) 为什么在从而bar不是移动构造函数返回时调用复制构造函数?
#include <iostream>
using namespace std;
class Alpha {
public:
Alpha() { cout << "ctor" << endl; }
Alpha(Alpha &) { cout << "copy ctor" << endl; }
Alpha(Alpha &&) { cout << "move ctor" << endl; }
Alpha &operator=(Alpha &) { cout << "copy asgn op" << endl; }
Alpha &operator=(Alpha &&) { cout << "move asgn op" << endl; }
};
Alpha foo(Alpha a) {
return a; // Move ctor is called (expected).
}
Alpha bar(Alpha …Run Code Online (Sandbox Code Playgroud) 阅读本文和本标准以及标准的23.3.6.5/1,其中最新的C++标准草案规定,当重新分配其元素时,实现者应优先使用非投掷移动构造函数T(T &&t) noexcept而不是const复制构造函数T(const T &t).手术std::vector<T>的结果push_back?关于引用绑定的重载决议是13.3.3.1.4/1吗?
编辑1
我在13.3.3.1.4/1上争论,原因如下:
重载决策选择要在语言中的七个不同上下文中调用的函数.[...] 调用构造函数以进行类对象的直接初始化(8.5)(13.3.1.3) ; [...]这些上下文中的每一个都以其自己独特的方式定义候选函数集和参数列表.但是,一旦确定了候选函数和参数列表,最佳函数的选择在所有情况下都是相同的:首先,候选函数的子集(具有适当数量的参数并满足某些其他条件的函数)是选择形成一组可行的功能(13.3.2).然后,基于将每个参数与每个可行函数的相应参数匹配所需的隐式转换序列(13.3.3.1)来选择最佳可行函数.
从为给定上下文(13.3.1)构造的候选函数集合中,选择一组可行函数,通过比较最佳拟合的参数转换序列(13.3.3),从中选择最佳函数.可行函数的选择考虑了除转换序列的排名之外的参数和函数参数之间的关系.
当类类型的对象被直接初始化(8.5),或从相同或派生类类型(8.5)的表达式进行复制初始化时,重载决策选择构造函数.
对于参数类型为参考的情况,请参见13.3.3.1.4.
当引用类型的参数直接(8.5.3)绑定到参数表达式时,隐式转换序列是标识转换,[...]
因此,我的结论是身份的转换结果在需要的优先顺序的要求T(T &&t) noexcept了T(const T &t).但是,我所争辩的另一方并不相信.所以,我在这里问.
编辑2
以下是23.3.6.5和13.3.3.1.4之间的联系:
首先,23.3.6.5要求以下内容std::vector:
[...]
void push_back(const T& …
问题很简单,标题里已经概括了。是否有任何特殊原因导致这种情况?鉴于对象可以有效地交换内部状态,我确信这里有一些我忽略的东西......
考虑这个类
class A {
public:
tracker tra;
A(tracker _t) : tra(_t) {}
};
Run Code Online (Sandbox Code Playgroud)
并通过调用它
A a {tracker()};
Run Code Online (Sandbox Code Playgroud)
创建的对象tracker()在被存储到之前永远不会被使用a.tra
为什么编译器不优化所有的复制结构?
。
跟踪器定义如下:
class tracker {
public:
void mark(const char* v) {
std::cout << v << ' ' << this << std::endl;
}
tracker() {
mark("con");
}
tracker(const tracker& o) {
mark("cpy");
}
tracker(tracker&& o) {
mark("mov");
}
~tracker() {
mark("des");
}
tracker& operator=(const tracker&) {
mark("=cp");
return *this;
}
tracker& operator=(tracker&&) {
mark("=mv");
return *this;
}
};
Run Code Online (Sandbox Code Playgroud) 将派生类标记为可移动而基类不可移动是否有意义\适合吗?
我知道这种不一致在 C++ 中是合法的,但它在实践中有意义\适合吗?
一般来说,我应该刻意保持这种一致性吗?
这种情况怎么样:当我打算将派生类标记为不可移动和不可复制时,我是否也应该将基类标记为不可移动和不可复制?
我做了几次测试才清楚。
这是第一个例子。由于基类是不可复制和不可移动的,因此派生类实际上是不可移动的,因为它有一个移动构造函数,这在我的期望中。提示:下面的代码片段无法编译。
#include <memory>
#include <string>
#include <iostream>
class Base {
public:
Base(){}
Base(const Base&) = delete;
Base(Base&&) = delete;
Base& operator=(const Base&) = delete;
Base& operator=(Base&&) = delete;
};
class Derived:public Base
{
public:
Derived(){}
Derived(const Derived&) = default;
Derived(Derived&&) = default;
Derived& operator=(const Derived&) = default;
Derived& operator=(Derived&&) = default;
};
int main()
{
Derived derived;
Derived derived1{std::move(derived)};
}
Run Code Online (Sandbox Code Playgroud)
这是第二个例子。基类是可复制且不可移动的,但派生类实际上是可移动的,因为在调用派生类的移动构造函数时,它会调用基类的复制构造函数,而不是基类的移动构造函数,即也在我的预料之中。提示:下面的代码片段效果很好。
#include …Run Code Online (Sandbox Code Playgroud) move-constructor ×10
c++ ×9
c++11 ×5
move ×4
constructor ×2
copy-elision ×2
c++14 ×1
c++17 ×1
deep-copy ×1
unique-ptr ×1
vector ×1
visual-c++ ×1