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

Ami*_*rsh 11 c++ undefined-behavior rvalue-reference unique-ptr c++11

从“死”的unique_ptr中使用operator *的返回值是不好的。

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

auto& ref = *std::make_unique<int>(7);
std::cout << ref << std::endl;
Run Code Online (Sandbox Code Playgroud)

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

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

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

std::cout << *std::make_unique<int>(7) << std::endl;
Run Code Online (Sandbox Code Playgroud)

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


旁注:当然,有些人仍然可以像下面这样编写错误的代码,但是更冗长地说“我是UB”,因此不太相关:

auto&& ref = *std::make_unique<int>(7);
std::cout << ref << std::endl;
Run Code Online (Sandbox Code Playgroud)

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

Nic*_*las 8

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

auto &ref = *(new int(7));
Run Code Online (Sandbox Code Playgroud)

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

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


Yeh*_* B. 4

好问题!

在不深入研究相关论文和设计讨论的情况下,我认为有几点可能是这个设计决定的原因:

  1. 正如 @Nicol Bolas 提到的,这就是内置(原始)指针的行为方式,因此“照做int”在这里应用为“照做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>());
}
Run Code Online (Sandbox Code Playgroud)

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


顺便说一句:我不确定auto&&说“我是 UB”是否更大声。相反,有些人会认为这auto&&应该是我们在许多情况下的默认设置(例如基于范围的 for 循环;甚至建议自动插入“简洁符号基于范围的 for 循环”(这没有被接受,但是仍然...))。让我们记住,rvalue-ref 具有与const &延长临时生命周期类似的效果(在已知限制内),因此它通常看起来不一定像 UB。