使变量不可复制的紧凑方法
Compact way to make a Variable non-copyable
我的代码中有几个类,我需要复制其中的对象。
但是,其中一些类使用我想在复制时跳过的数据,例如所有者指针。
到目前为止,我发现这样做的唯一方法是避免完全复制并在每次需要复制时手动构造一个全新的对象,或者将所有者指针包装在一个私有的、不可复制的结构中,如下所示:
class MyClass {
// non-copyable owner
struct Owner {
Owner() = default;
~Owner() = default;
Owner(const Owner& o) = delete;
Owner& operator=(const Owner& o) = delete;
SomeOtherClass* pointer = nullptr;
};
Owner owner = Owner();
}
但是,这样做似乎有点冗长。
(注意我不想使用std::unique_ptr
因为我不想在解构对象时解构所有者(
有没有更紧凑/高效/可读的方法?
编辑:default
标记的构造函数等的无效源于我没有使用默认值初始化所有者。憨。
编辑2:也许我应该澄清一些事情:所有者指针应指向拥有MyClass
的对象,作为拥有的对象引用其所有者的一种方式。反之则不然。这就是为什么我希望不复制指针,因为应该复制此对象的具有不同所有者的对象不应更改它拥有的对象。
公认的答案使我能够做到这一点。我希望这能消除一些困惑。
您可以将std::unique_ptr
与不执行任何操作的删除器一起使用。
template<typename T>
struct nop_deleter<T> {void operator()(T*){};};
class MyClass {
std::unique_ptr<SomeType, nop_deleter<SomeType>> owner;
};
然后,"拥有"对象不会被解构。(在这一点上,它不再真正拥有了,但这只是我的迂腐(。
如果您希望它更具可读性,您可以随时为类型设置别名:
template<typename T>
struct nop_deleter<T> {void operator()(T*){};};
template<typename T>
using Owner = std::unique_ptr<T, nop_deleter<T>>;
class MyClass {
Owner<SomeType> owner;
};
由于 elision 的工作方式,以及在较小程度上容器的工作方式,复制(和移动(需要语义。
如果省略了副本或移动,行为良好的值类型的行为不应有惊人的不同。 它们在存放在std::vector
中时也应该表现良好。
您的描述似乎很像auto_ptr
在我们获得移动语言支持之前被黑客入侵成为unique_ptr
。 这是一个聪明的想法,变成了一个坏主意的怪物,auto_ptr
是标准库中为数不多的已弃用的功能之一,因为它被证明是一个坏主意。
将类型的"状态"与其标识分开。 例如,所有权是一种身份特征,而高度是状态特征。
将状态存储在类型中的struct
中。
标识存储在你的类型中,或者存储在另一个子结构中。 标识子结构应具有=delete
复制/移动操作。
提供一种方法来创建具有新标识但状态相同的新对象。 如果对象类型为 Foo,其状态为 FooState,则您将拥有一个Foo(FooState)
显式构造函数。
有一个生成Foo
的状态副本的方法。
不允许复制Foo
。 复制Foo
的语义,其中标识在复制后更改(被清除(不是正常的复制语义。Foo
应该是不可复制的。
移动语义是可能的;在这种情况下,你会向你的所有者报告你的位置正在改变(通过一些界面( - 比如:
if (owner)
owner->child_moving( &old, this );
for( auto* child:children )
child->parent_moving( &old, this );
这允许父/子更新其所有者/子指针。
除非你做这种事情,否则你不想假装有价值语义;删除你的复制/移动操作,而不是实现疯狂的操作。
也许像工厂方法这样的东西至少可以避免编译时的复制部分?
// a method of MyClass
Owner * MyOwner()
{
// check if created
if(uidMap.find(parentObjectUid )==uidMap.end())
{
// create if needed
uidMap.insert(std::pair<size_t,Owner *>(parentObjectUid ,new Owner()));
// you can even use shared/unique ptr instead of new Owner()
// if you want automatic garbage
return uidMap[parentObjectUid ];
}
else
{
return uidMap[parentObjectUid];
}
}
然后创建 UID 可能需要不需要的单调或类似的全局同步制作。因此,只要使用相同的 uid 复制 MyClass,两个副本将具有相同的 Owner 对象。
MyClass a;
MyClass b;
Owner * o = a.MyOwner(); // => creates
Owner * p = a.MyOwner(); // => uses
Owner * q = b.MyOwner(); // => creates another
a = b; // doesn't copy anything Owner
唯一 ID 可以是递增的 64 位整数。
不拥有资源的所有者?听起来你在代码中撒谎,如果我在代码中看到这一点,我会认为你有一个错误。
也就是说,unique_ptr确实是处理它的最佳方法。您可以为其提供一个自定义析构函数来关闭资源(如关闭文件句柄(。从可读的角度来看,这将是最容易理解的,而不是使用自定义类。
我不知道你如何定义效率,但是,假设运行时效率,它们没有任何影响。
最紧凑的编写方式是从 NonCopyable 类继承,boost 有一个,但是如果你不想要这种依赖关系,编写起来非常容易。
从可读的角度来看,我实际上会使用您的原始实现的变体,即:遵循 5 法则:在您的类中指定 Ctor、复制 Ctor、移动 ctor、复制分配和移动分配。
- 简单可复制与可简单复制
- reinterpret_cast,只读访问,简单的可复制类型,会出什么问题?
- 对于参加可复制和可移动类的访问者来说,应该有多少过载?
- 可变参数宏:无法通过"..."传递非平凡可复制类型的对象
- 为什么 std::atomic<std::string> 会给出微不足道的可复制错误?
- 我可以隐式地创建一个琐碎的可复制类型吗
- 是std::memcpy在不同的可复制类型之间的未定义行为
- 为什么一对常量是微不足道的可复制的,而对不是?
- 在一个微不足道的可复制结构中,移动语义应该实现吗?
- 防止作用域枚举可复制/可移动
- C :对象上的可复制视图
- 防御性地应用 std::move 到平凡可复制的类型是否不可取
- 为什么 std::function 本身是可复制构造的类型?
- C++不可复制的 lambda 的行为是可复制的
- 如何使用单个解锁方法(可称为读取器或写入器)实现C++读写器锁?
- 错误:无法通过'...'传递非平凡可复制类型的对象'class boost::filesystem::path'
- 不能让类是微不足道的可复制的。我做错了什么?
- 有什么方法可以为Emscripten构建Zeromq
- 使用临时存储区复制普通的可复制类型:允许吗
- 有没有一种方法可以将编译器防火墙(Pimpl)和默认可复制性的优点结合起来