首页 > 解决方案 > 从函数返回一个 RAII 对象

问题描述

在 C++14 中,我试图定义一个“RAII 工厂”函数,如下所示:

// Here, "ScopedReseource" is a plain RAII class, managing some resource.

ScopedResource factory(/* args */) {
    return ScopedReseource(/* args */);
}

客户使用情况将是:

{
    auto scoped = factory(/* args */));
    // use scoped...
}

据我了解,C++ 14 中的语言不保证复制省略,因此这是不可靠的。特别是,它可能导致 ScopedResource 的析构函数在factory()函数结束时被调用(并在调用点生成一个新副本)。我不想要那个。

当然,传统的客户端代码可以正常工作:

{
    ScopedResource scoped(/* args */);
    // use scoped...
}

现在,我尝试删除ScopedResource的复制构造函数 ( =delete),并定义一个移动构造函数。代码编译,它只构造/破坏一次,这是我想要的——但它是可移植的吗?

所以,问题:

还是我在这里遗漏了其他一些细微差别?

标签: c++c++14c++17

解决方案


据我了解,C++ 14 中的语言不保证复制省略,因此这是不可靠的。特别是,它可能导致在 factory() 函数结束时调用 ScopedResource 的析构函数(并在调用点生成一个新副本)。我不想要那个。

为什么不?

如果您的类型是可移动的,那么这意味着被移动的对象将包含一个“空”资源。也就是说,它当前不会与资源相关联。因此,它的析构函数不会做任何事情,被管理的资源仍然存在。

所以在 C++14 中按值返回这样的对象是没有问题的。是的,析构函数可能会被调用,但既然它什么都不做,谁在乎呢?

在 C++17 之前返回一个 RAII 对象(析构函数执行我想要完成一次的特殊工作)我是对的吗?

不,你不正确。只要您将对象编码为具有表示没有资源的“空”状态(这是仅移动类型的标准),“特殊工作”将仅由单个对象完成。可能会调用多个析构函数,但只有一个这样的调用会完成释放资源的重要部分。

这是关于正确实现仅移动类型,而不是关于复制省略。


推荐阅读