返回值的初始化应忽略自动对象的恒常性

Initialization of a return value should ignore constness of automatic object

本文关键字:对象 常性 初始化 返回值      更新时间:2023-10-16

坚持常量的正确性,您可能希望将类型unique_ptr的本地对象设置为常量,如下所示,而T在这里是一些不重要的类型:

unique_ptr<T> foo() {
    const unique_ptr<T> p = make_unique<T>(...);
    ... using p's pointee ...
    return p;
}
不幸的是,它无法编译,因为

返回值无法初始化,因为unique_ptr没有复制构造函数,并且移动构造函数不可行,因为p是常量。

如果C++标准会说当 return 语句的"操作数"是一个自动对象时,那么它的潜在恒常性将被忽略呢?初始化返回值后无法引用自动对象,因此其恒常性现在无关紧要。在其析构函数中,const 对象也可以修改,因此它实际上只是关于定义常量结束的位置:就在析构函数之前或在此特殊情况下在返回值的构造函数之前。

然而,"不能引用"并不是全部事实:在初始化返回值后排序的其他局部变量的析构函数可能会引用它。但我认为他们不可能关心 const 对象被修改了。他们通过指针或对 const 的引用来了解对象,即从他们的角度来看,他们只知道他们不允许修改它,他们无法知道其他人是否被允许修改它。

您认为对C++标准进行这样的更改是个好主意吗?或者您是否在我下面给出的解决方案之外看到其他解决方案?

仅供记录:非解决方案和其他解决方案:

  • 你不能return unique_ptr<T>(p.get()),因为现在你有两个unique_ptr,每个都认为它是唯一的所有者。
  • 您无法return unique_ptr<T>(p.release()),因为p是常量,而release()是非常量成员方法。
  • return move(p)没有用,因为它不会删除恒常性,因此仍然无法调用移动构造函数。
  • 编辑:正如Chris Beck指出的那样,return const_cast<unique_ptr<T>&&>(p)也不是解决方案,因为修改const对象是未定义的行为(标准N4140中的7.1.6.1 p4(。

由于 unique_ptr 是仅移动类型,因此按值返回将(假设(调用返回值的移动构造函数。但是由于从中移动是一种破坏性操作,因此您无法真正从 const 对象中移动。

但我认为 C++17 的"保证复制省略"(http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0135r0.html(会对此有所帮助。基本上,这将允许从函数(例如 const unique_ptr(按值返回不可移动的类型。再一次,我担心,你将不得不等待新标准

我认为这样的更改将是一个坏主意。我有点明白你来自哪里,但你对你的问题的解决方案只会创造一个目前已经解决的新问题。

我认为我们需要考虑为什么我们一开始就const对象。

它关乎安全性,并确保给定变量不会发生错误的事情。通过制作unique_ptr const,您实际上是在说您不希望在对象上发生任何所有权操纵。您仍然可以操作它管理的对象:

std::unique_ptr<T> foo()
{
    // const object means no calling non-const member functions
    // which means no changing the ownership of the managed pointer
    const std::unique_ptr<int> p = std::make_unique<int>();
    // but we still get to manipulate the thing being pointed to:
    *p = 6;
    return std::move(p); // compile error - changing ownership
}

在这种情况下,您可能希望进行unique_ptr const以防止意外退回,而不是您想要返回的其他unique_ptrunique_ptr const 设置为常量实际上可以防止它被错误地返回。因此,我们现在能做的是使我们所有的unique_ptr对象都const除了我们希望将其所有权归还给调用方的对象

您的更改将使该保护无效,并且一开始就使对象const的意义会降低。