如何在 C++ 中创建临时变量

How to create a temporary variable in C++

我有一个函数 return 引用我的 class "record".

的一个实例
record& get_record(int key) {
    return lookup(key);
}

这是有效的,它 return 是参考而非价值。现在我稍微修改一下。

record& get_record(int key) {
    if (valid(key))
        return lookup(key);
    else {
        record x;
        x.valid=false;
        return x; //Here I really want to return a temporary variable
                  // and not a reference to a local variable.      
    }
}

return引用局部变量不是一个好主意,这对吗?以及如何 return x 使其成为临时变量?

Is it correct that returning a reference to a local variable is not a good idea?

是的。退出函数时本地对象将被销毁,因此返回的引用始终悬空。

您可以将 x 设为 static 变量。

record& get_record(int key) {
    if (valid(key))
        return lookup(key);
    else {
        static record x;
        x.valid=false;
        return x;
    }
}

请注意,返回的引用将始终引用同一对象,即 x

这比坏主意还糟糕, 并且在大多数情况下会导致崩溃。这个不好(TM)

您可以做的是更改 get_record 的 return 类型,使其 return 成为智能指针。如果 key 有效,它 return 是指向它的观察者指针。否则,它 return 是一个指向新创建对象的智能指针:

#include <memory>
#include <iostream>

struct record { int n; } some_record{42};

std::shared_ptr<record> get_record(bool b)
{
    if (b == true) {
        return std::shared_ptr<record>{&some_record, [](record*){}}; // see explanation ^1
    }
    return std::shared_ptr<record>{new record{0}};
}

int main()
{
    std::cout << get_record(true)->n << "\n";  // this is some_record
    std::cout << get_record(false)->n << "\n"; // this is a temporary
}

1) 关于 [](record*){}:这个作为 std::shared_ptr::shared_ptr() 的第二个参数给出的无操作 lambda 在智能指针被销毁时被调用。它替换了 std::shared_ptr 的默认删除器,其行为是在拥有的指针上调用 delete


关于为什么你的设计有缺陷。事实上,使 get_record return 成为参考会使它不一致。你想要的是:

  • 如果 key 有效 return 对 existing/permanant 对象的引用,并且
  • return 否则为临时对象。

这两个是互斥的,你的函数没有意义:get_record return 在语义上是什么?

正如其他人已经详细说明了为什么返回对本地的引用是不好的,我只是提供一个替代方案:异常。尽管您可以编写自定义异常,但可以说 std::out_of_range exception would seem in place (as the key lies outside of the range of valid keys, which is exactly what std::map::at 可以)。

看看:How to throw a C++ exception

record& get_record(int key)
{
  if (valid(key))
  {
    return lookup(key);
  } else {
    throw std::out_of_range("The provided key is invalid");
  }
}

显然,您现在必须捕获调用 get_record 的代码中的异常,否则您的程序仍将终止。

如果允许您修改 get_record 函数,您可以将 return 类型更改为指向记录的指针而不是对记录的引用。

record* get_record( int key )
{
    if( valid( key ) ) return &lookup( key ); 
    else               return nullptr;
 }

但是,这种方法需要两个保证:

  • 查找函数必须return记录
  • 的引用
  • 查找 return 的记录在查找 return 时必须仍然存在(例如,记录是某种容器的元素,查找 return 是它的引用)

返回引用本身不会产生未定义的行为,但如果您尝试修改它,那么您会。

Accessing an object outside of its lifetime is undefined behavior.

int* foo(void) {
    int a = 17; // a has automatic storage duration
    return &a;
}  // lifetime of a ends
int main(void) {
    int* p = foo(); // p points to an object past lifetime ("dangling pointer")
    int n = *p; // undefined behavior
}

http://en.cppreference.com/w/c/language/lifetime

如果您可以访问 C++17,则可以使用 std::optional. Note the use of std::reference_wrapper 来实现它,因为在 std::optional 中使用引用会使您的程序格式错误。

std::optional<std::reference_wrapper<record>> get_record(int key) {
    if (valid(key))
        return std::optional<std::reference_wrapper<record>>(lookup(key));
    else
        return std::nullopt;
}

如果没有 C++17,您可以 return 指向您的记录的指针:

record* get_record(int key) {
    if (valid(key))
        return &lookup(key);
    else
        return nullptr;
}

或者,如果您愿意,可以保留引用 return 类型,并抛出异常以指示缺少记录。尽管这是我最不喜欢的方法,因为它可以很容易地调用 get_record 而无需包装在 try / catch.

record& get_record(int key) {
    if (valid(key))
        return &lookup(key);
    else
        throw std::out_of_range("Invalid record key!");
}