为什么右值unique_ptr的运算符*返回左值?

时间:2019-07-24 14:36:16

标签: c++ c++11 undefined-behavior unique-ptr rvalue-reference

使用“死”的unique_ptr中的operator *的返回值是错误的。

以下代码可以编译,但是结果当然是未定义行为:

auto& ref = *std::make_unique<int>(7);
std::cout << ref << std::endl;

为什么标准没有将std :: unique_ptr的右值的operator *的返回值作为内部值的 rvalue 而不是 lvalue ,像这样:

// could have been done inside unique_ptr
T& operator*() & { return *ptr; }
T&& operator*() && { return std::move(*ptr); }

在这种情况下,可以正常工作:

std::cout << *std::make_unique<int>(7) << std::endl;

但是开头的代码无法编译(无法将右值绑定到左值)。


旁注:当然,有些人仍然可以像下面那样编写不好的代码,但是它说的是“我是UB”,因此不太相关:

auto&& ref = *std::make_unique<int>(7);
std::cout << ref << std::endl;

是否有充分的理由让std :: unique_ptr右值上的operator *返回左值ref?

2 个答案:

答案 0 :(得分:7)

就涉及的值类别和基本概念而言,您的代码等效于此:

auto &ref = *(new int(7));

new int(7)产生一个指针对象,该对象是一个prvalue表达式。取消引用该prvalue会生成一个左值表达式。

无论指针对象是右值还是左值,将*应用于指针将导致左值。那不应该仅仅因为指针是“智能”而改变。

答案 1 :(得分:2)

好问题!

在不深入研究相关论文和设计讨论的情况下,我认为可能有一些要点可能是做出此设计决定的原因:

  1. 就像@Nicol Bolas提到的那样,这就是内置(原始)指针的行为,因此此处将“ do as int的用法”和“ do as int*的用法”一样应用“。

    这类似于unique_ptr(和其他库类型)不会传播const的事实(这又就是我们添加propagate_const的原因)。

  2. 以下代码段如何处理?它不符合您的建议更改,但它是不应被阻止的有效代码。

class Base { virtual ~Base() = default; };
class Derived : public Base {};
void f(Base&) {}

int main()
{
    f(*std::make_unique<Derived>());
}

({godbolt-如果我们的operator*重载被注释掉,它将编译)


请注意:我不确定auto&&会大声说“我是UB”。相反,有人会认为auto&&在许多情况下应该是我们的默认设置(例如,基于范围的for循环;甚至建议自动插入“基于范围的简短表示法的for循环”(不被接受,但仍然...))。让我们记住,rvalue-ref具有与const &类似的作用,即延长临时生存期(在已知限制内),因此通常不一定看起来像UB。

相关问题