依赖项没有复制构造函数或赋值运算符时的 C++ 初始化器列表

问题描述

方法

我有一些内部包含 std::mutex 的 Foo 类型。

class Foo {
  std::mutex m_;
};

我又写了一个类吧。 Bar 将 Foo 作为成员和构造函数,如下所示(注意 - 此代码无法编译):

class Bar {
  Bar(Foo foo) : foo(foo) {}
...
private:
  Foo foo;
};

我的 IDE 抱怨:

调用 Foo 隐式删除的复制构造函数。 'Foo' 的复制构造函数被隐式删除,因为字段 'm_' 有一个删除的复制构造函数

方法 2

然后我尝试做这样的作业:

class Bar {
  Bar(Foo fooIn) { foo = fooIn; }
...
private:
  Foo foo;
};

但这也说明:

'Foo' 类型的对象无法赋值,因为它的复制赋值运算符被隐式删除

方法三(使用指针)

我可以让它工作的唯一方法是这样的:

class Foo {
private:
    std::mutex m_;
};

class Bar {
    Bar(std::unique_ptr<Foo> fooIn) : foo(std::move(fooIn)) {}
private:
    std::unique_ptr<Foo> foo;
};

我知道智能指针有助于内存管理。但是让我们把这个功能放在一边。

在我上面的例子中,不使用智能指针和使用智能指针的主要区别在于智能指针具有间接性——它指向堆上的一个对象。对吗?

理解这一点的正确思维模型是什么?是不是因为 Foo 有一个删除的复制构造函数删除的复制赋值,我唯一的选择是让它在堆上构造,然后通过间接(通过指针)工作?

这样对吗?还是我完全没了?

解决方法

是不是因为 Foo 有一个删除的复制构造函数和删除的复制赋值,我唯一的选择是让它在堆上构造,然后通过间接(通过指针)工作?

接近,但不完全。

因为互斥锁是不可复制的,所以默认情况下 Foo 也是不可复制的。您正在尝试复制,这是不可能的。

使用间接引用来引用存储在别处的 Foo 是可能的。但是该 Foo 没有必要具有动态存储来实现这一点。不可复制性也不意味着需要间接...除非您想防止不可复制性传播到封闭类。

互斥锁唯一的构造函数是默认构造函数。因此,您可以合理地做的只是默认初始化 Foo。一个最小的例子:

struct Bar {
    Foo foo;
    // no need to declare the default constructor
    // it is generated implicitly
};

Bar bar; // this just works
,

如果您检查互斥锁的实现方式,您会看到它的复制构造函数和复制赋值运算符被删除,但移动构造函数和移动赋值运算符没有删除,这就是您的智能指针示例有效的原因。

我唯一的选择是在堆上构造它,然后通过间接(通过指针)工作?

不,这不是唯一的方法。如果您稍微更改您的第一个示例以使用引用,那也将起作用(并且 foo 不是在堆上构造的):

class Foo {
    std::mutex m_;
};

class Bar {
    Bar(Foo& foo) : foo(foo) {}

private:
    Foo& foo;
};