std::make_unique作为C++中临时对象未绑定到非常量引用的解决方法

wdebmtf2  于 2023-03-25  发布在  其他
关注(0)|答案(1)|浏览(166)

在下面的代码片段中,调用mc.function1()成功,并将默认值传递给function1。
我想知道为什么Visual Studio不认为 *std::make_unique< SomeClass >()是临时对象。如果我将SomeClass()设置为默认值,那么Visual Studio会正确地将其捕获为编译时错误,因为它是一个临时对象,我们试图将其绑定到非常量引用。
为什么Visual Studio 2019将 *std::make_unique< SomeClass >()视为非临时对象?
根据C++ ISO标准,*std::make_unique< SomeClass >()是否被视为临时对象?
还是编译器只是对它宽容?

class SomeClass
{
};

class MyClass
{
public:

    void function1 (SomeClass &obj = *std::make_unique<SomeClass>()) { }
    //void function2 (SomeClass &obj = SomeClass()) { } //compile error
};

int main()
{
    MyClass mc;
    mc.function1();
}

下面的function1编译并执行良好。

void function1 (SomeClass &obj = *std::make_unique<SomeClass>()) { }

我认为它不应该编译,因为 *std::make_unique()是一个临时对象,它被绑定到非常量引用。但是它构建和运行良好。

11dmarpk

11dmarpk1#

一个对象是否可以被绑定到一个引用与它是否是一个临时对象无关。这纯粹是一个用来初始化引用的表达式的值类别是什么的问题。如果值类别是左值,那么左值引用可以被绑定到表达式引用的对象。
std::unique_ptroperator*有一个左值引用作为返回类型。对一个返回左值引用的函数的函数调用是一个左值。所以一个左值引用可以绑定到*的结果引用的对象。
std::unique_ptr在这里没有任何特殊之处。您可以编写自己的函数来实现它:

template<typename T>
T& as_lvalue(T&& t) {
    return t;
}

现在你可以写了

SomeClass& x = as_lvalue(/*whatever*/);

不管/*whatever*/有什么值类别,引用现在都可以绑定到它。
不能将右值绑定到非const左值引用的规则纯粹是为了保护你,因为大多数时候这样做在语义上是错误的,并且/或者有更清晰的替代方案(例如const左值引用或右值引用)。如果你想忽略这种保护,C++不会阻止你。
但是考虑一下你是否真的想这么做。即使你设法将一个引用绑定到一个临时对象,这通常也不会延长临时对象的生存期。例如,如果上面的/*whatever*/引用了一个临时对象,那么这个临时对象的生存期将在初始化引用的那一行之后结束。所以这个引用将立即悬空。
类似地,在你的例子中,用std::make_unique创建的临时对象将只存在到函数调用结束。如果你将引用存储在其他地方或从函数返回它,它将是悬空的。那么,通过引用修改它有什么意义呢(非const左值引用),如果调用者永远不能使用你存储到其中的值?它可以只是一个const左值引用。

相关问题