如何在 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!");
}
我有一个函数 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
。
这比坏主意还糟糕,
您可以做的是更改 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!");
}