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

时间:2018-05-25 07:52:43

标签: c++ reference temporary

我有一个函数返回对我的类“record”的实例的引用。

record& get_record(int key) {
    return lookup(key);
}

这是有效的,它返回引用而不是值。现在我稍微修改一下。

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.      
    }
}

返回对局部变量的引用是不是一个好主意?以及如何以一种临时变量的方式返回x?

5 个答案:

答案 0 :(得分:7)

  

返回对局部变量的引用是不是一个好主意?

是。离开函数时,本地对象将被销毁,因此返回的引用总是悬空。

您可以将x变为static变量。

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

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

答案 1 :(得分:6)

这比一个坏主意it is undefined behavior更糟糕,导致大多数情况发生崩溃。这很糟糕(TM)。

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

#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()的第二个参数给出的no-op lambda。它取代了std::shared_ptr的默认删除器,其行为是在拥有指针上调用delete

关于您的设计存在缺陷的原因。实际上,使get_record返回引用会使其不一致。你想要的是:

  • 如果key有效,则返回对现有/永久对象的引用,
  • 否则返回临时对象。

这两者是相互排斥的,你的功能没有意义:get_record在语义上有什么回报?

答案 2 :(得分:1)

如果允许修改 get_record 功能,可以将返回类型更改为指向记录而不是引用记录。

<a href="javascript:history.go(-1)" class="back" >
            <asp:Label ID="backLabel" runat="server" Text="Back" meta:resourcekey="backLabelResource1" />
        </a>

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

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

答案 3 :(得分:0)

正如其他人已经详细说明为什么返回对本地的引用是坏的,生病只是提供另一种选择:例外。虽然您可以编写自定义异常,但可能会出现std::out_of_range异常(因为密钥位于有效密钥范围之外,这正是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的代码中捕获异常,否则您的程序仍将终止。

答案 4 :(得分:0)

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

  

访问其生命周期之外的对象是未定义的行为。

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实现它。请注意使用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,您只需返回指向记录的指针:

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

或者如果您愿意,可以保留引用返回类型,并抛出异常以指示缺少记录。虽然这是我最不喜欢的方法,因为它可以轻松调用get_record而无需包裹try / catch

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