C++ 返回值的初始化应忽略自动对象的常量

C++ 返回值的初始化应忽略自动对象的常量,c++,c++11,return,move-semantics,const-correctness,C++,C++11,Return,Move Semantics,Const Correctness,为了保持常量的正确性,您可能希望将类型的本地对象设置为唯一的\u ptr常量,如下所示,而T在这里是一些不重要的类型: unique_ptr<T> foo() { const unique_ptr<T> p = make_unique<T>(...); ... using p's pointee ... return p; } unique_ptr foo(){ const unique_ptr p=make_unique(…); …

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

unique_ptr<T> foo() {
    const unique_ptr<T> p = make_unique<T>(...);
    ... using p's pointee ...
    return p;
}
unique_ptr foo(){
const unique_ptr p=make_unique(…);
…使用p的指针。。。
返回p;
}
不幸的是,它无法编译,因为返回值无法初始化,因为
unique\u ptr
没有复制构造函数,而移动构造函数不可行,因为
p
是常量

如果C++标准会说,当返回语句的“操作数”是一个自动对象时,它的潜在稳定性会被忽略吗?自动对象在返回值初始化后不能被引用,因此它的常量现在不相关。在它的析构函数中,const对象也可以被修改,所以它实际上只是定义constness结束的位置:在析构函数之前,或者在这个特殊情况下在返回值的构造函数之前

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

你认为对C++标准的这种改变会是个好主意吗?或者,除了我在下面给出的解决方案之外,您还看到了其他解决方案吗

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

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

    • 我认为这样的改变是个坏主意。我有点明白你是从哪里来的,但是你对你的问题的解决方案只会产生一个目前已经解决的新问题

      我想我们首先要考虑为什么我们要创建对象<代码> const >

      这是关于安全和确保给定变量不会发生错误的事情。通过创建一个
      唯一的\u ptr
      常量
      ,实际上就是说您不希望在对象上发生任何所有权操纵。您仍然可以操作它管理的对象:

      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
      }
      
      std::unique_ptr foo()
      {
      //常量对象表示不调用非常量成员函数
      //这意味着不更改托管指针的所有权
      const std::unique_ptr p=std::make_unique();
      //但我们仍然可以操纵被指向的东西:
      *p=6;
      返回std::move(p);//编译错误-更改所有权
      }
      
      在这种情况下,您可能希望使您的
      唯一\u ptr
      常量
      避免意外返回它,而不是确实想要返回的另一个
      唯一\u ptr
      。使
      唯一\u ptr
      常量实际上可以防止它被错误返回。因此,我们现在可以做的是使所有的
      对象都是唯一的\u ptr
      对象
      const
      ,除了我们希望将其所有权返回给调用方的对象之外


      您的更改将使该保护无效,并且在开始时使对象
      常量
      没有什么意义。

      由于unique\u ptr是一种仅移动的类型,按值返回将(假设)调用返回值的移动构造函数。 但由于从中移动是一种破坏性的操作,所以不能真正从常量对象中移动

      但我认为C++17的“保证的副本省略”(assured copy elision)会对此有所帮助。 基本上,这将允许不可移动的类型通过函数的值返回,例如const unique_ptr。
      再一次,我担心,您将不得不等待新标准的出台

      当然,另一个解决方案是首先不要将其设置为常量?但从我目前的观点来看,我不得不放弃常量正确性,没有充分的理由。如果您想通过释放对象作为返回值来修改对象,那么将其设置为常量是不正确的。您能否更详细地解释一下
      const unique\u ptr
      的好处是什么?@M.M与标记任何局部变量const的好处相同。有些人认为const应该是默认值(如在SML中)。我同意我真正想要的是能够表示名为
      p
      的本地对象是返回值-应该只有一个对象,而不是两个。也就是说,我不想被迫使用我需要的模型来初始化另一个对象的返回值,这里是
      p
      。当前标准中的复制省略没有帮助,因为即使使用它,概念上仍然有两个对象,而不是一个。考虑到资源管理类,使用const,我确实想表示在对象的生命周期内(即直到析构函数调用)不应修改所有权。