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

std::make_unique as workaround for temporary object not binding to non const reference in C++

提问人:Prasad Patil 提问时间:3/24/2023 最后编辑:Prasad Patil 更新时间:3/24/2023 访问量:221

问:

在下面的代码片段中,调用 mc.function1() 成功,默认值传递给 function1。

我想知道 visual studio 如何不将 *std::make_unique< SomeClass >() 视为临时对象。其中,如果我将 SomeClass() 设置为默认值,那么 Visual Studio 会正确地将其捕获为编译时错误,因为它是我们试图绑定到非常量引用的临时对象。

为什么 Visual Studio 2019 将 *std::make_unique< SomeClass >() 视为非临时对象?

根据 ISO C++标准,*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() 是绑定到非常量引用的临时对象。但它构建和运行良好。

C++ 参考 临时对象

评论

1赞 Some programmer dude 3/24/2023
void function3 (const SomeClass &obj = SomeClass())
0赞 Some programmer dude 3/24/2023
“构建良好”并不意味着代码实际上有效。您的参考将悬空,因为临时对象将被迅速销毁。
0赞 Marek R 3/24/2023
当表达完成评估时,暂时结束的生命。因此,只要函数调用完成,您的默认参数就会一直存在。此外,您还使这变得过于复杂:.void function3 (const SomeClass &obj = {})

答:

2赞 user17732522 3/24/2023 #1

对象是否可以绑定到引用与它是否为临时对象无关。这纯粹是用于初始化引用的表达式的值类别的问题。如果值类别为 lvalue,则可以将左值引用绑定到表达式引用的对象。

operator*OF 具有左值引用作为返回类型。对返回左值引用的函数的函数调用是左值。因此,左值引用可以绑定到结果所引用的对象。std::unique_ptr*

std::unique_ptr在这里没有任何特别之处。您可以编写自己的函数来执行此操作:

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

现在你可以写

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

无论哪个值类别具有什么值,引用现在都可以绑定到它。/*whatever*/

你不能将右值绑定到非左值引用的规则纯粹是为了保护你,因为大多数时候这样做在语义上是错误的,和/或者有更明确的替代方案(例如 左值引用或右值引用)。如果你想忽略这种保护,C++ 不会阻止你。constconst

但是考虑一下你是否真的想这样做。即使您设法将引用绑定到临时对象,这通常也不会延长临时对象的生存期。例如,如果上面引用的是一个临时对象,则该临时对象的生存期将在初始化引用的行之后结束。因此,引用将立即悬空。/*whatever*/

同样,在您的示例中,创建的临时对象将仅存在到函数调用结束。如果将引用存储在其他位置或从函数返回它,它将悬空。那么,如果调用者永远无法将存储的值使用到其中,那么能够通过引用(非左值引用)修改它有什么意义呢?它可以只是一个左值引用。std::make_uniqueconstconst

评论

0赞 Prasad Patil 3/24/2023
int* arr = 新 int[1];arr[0] = 1;std::unique_ptr<int[]> ptr(新int[1]{10});ptr = arr; // error: rvalue 在这种情况下,operator 不返回 lvalue
0赞 Prasad Patil 3/24/2023
如果托管对象是左值引用,则 std::unique_ptr 的 operator* 的返回类型为左值。否则,它是一个右值。
0赞 user17732522 3/24/2023
@PrasadPatil 输入注释时请附上代码,否则它将无法正确呈现,我看不出您要输入的内容。但我不知道你的意思,std::unique_ptr::operator* 总是返回单对象变体的左值引用,而数组变体不存在。`
0赞 Prasad Patil 3/24/2023
我认为,*std::make_unique<SomeClass>() 是一个右值,因为它返回了一个临时对象。
0赞 user17732522 3/24/2023
@PrasadPatil 是一个右值(特别是 PRVALUE)表达式,因为具有非引用返回类型。但是是一个左值表达式,因为 的返回类型是左值引用。请参阅 en.cppreference.com/w/cpp/language/value_category(固定链接)。std::make_unique<SomeClass>()std::make_unique*std::make_unique<SomeClass>()std::unqiue_ptr::operator*