返回常量引用参数时出现问题
我知道为什么以下内容不能正确工作,所以我不会问为什么。但我感觉不好的是,在我看来,这是一个非常大的编程障碍。
#include <iostream>
#include <string>
using namespace std;
string ss("hello");
const string& fun(const string& s) {
return s;
}
int main(){
const string& s = fun("hello");
cout<<s<<endl;
cout<<fun("hello")<<endl;
}
第一个 cout 将不起作用。第二个cout将会。
我的担忧如下:
是否无法想象方法实现者想要返回一个常量引用且不可避免的参数的情况?
我认为这是完全可能的。
在这种情况下,您会用 C++ 做什么?
谢谢。
I know why the following does not work correclty, so I am not asking why. But I am feeling bad about it is that it seems to me that it is a very big programming hindrance.
#include <iostream>
#include <string>
using namespace std;
string ss("hello");
const string& fun(const string& s) {
return s;
}
int main(){
const string& s = fun("hello");
cout<<s<<endl;
cout<<fun("hello")<<endl;
}
The first cout will not work. the second cout will.
My concern is the following:
Is it not possible to imagine a situation where a method implementor wants to return an argument that is a const reference and is unavoidable?
I think it is perfectly possible.
What would you do in C++ in this situation?
Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
在 C++ 中,建立对象的生命周期非常重要。一种常见的技术是为每个对象确定一个“所有者”。所有者负责确保该对象在需要时就存在,并在不需要时将其删除。
通常,所有者是另一个对象,它将所拥有的对象保存在实例变量中。处理此问题的其他典型方法是使其成为全局变量、类的静态成员、局部变量或使用引用计数指针。
在您的示例中,字符串对象没有明确的所有权。它不属于 main() 函数,因为它不是局部变量,并且没有其他所有者。
In C++, it is important to establish the lifetimes of objects. One common technique is to decide upon an "owner" for each object. The owner is responsible for ensuring that the object exists as long as it is needed, and deleting it when not needed.
Often, the owner is another object that holds the owned object in an instance variable. The other typical ways to deal with this are to make it a global, a static member of a class, a local variable, or use a reference-counted pointer.
In your example, there is no clear ownership of the string object. It is not owned by the main() function, because it is not a local variable, and there is no other owner.
我感受到你的痛苦。我发现其他情况下,返回 const 引用似乎是正确的做法,但也存在其他丑陋的问题。
幸运的是,这个微妙的问题在 c++0x 中得到了解决。始终按值返回。新的移动构造器将使事情变得如你所愿。
I feel your pain. I've found other situations where returning a const reference seemed the right thing to do, but had other ugly issues.
Luckily, the subtle gotcha is solved in c++0x. Always return by value. The new move constructors will make things a fast as you could wish.
该技术是有效的并且一直在使用。然而,在您的第一个示例中,您将 const char* 转换为临时临时变量 std::string 并尝试返回它,这不是与返回存储在其他地方的对象的常量引用相同。在第二个示例中,您正在做同样的事情,但是您正在使用临时对象被销毁之前的结果,在这种情况下这是合法但危险的(请参阅您的第一个案例。)
更新:请允许我澄清一下我的答案。我是说问题在于临时对象的创建,而不是正确处理所创建对象的生命周期。该技术是一项很好的技术,但它(以及许多其他好的技术)需要满足函数的前置条件和后置条件。这个负担的一部分落在函数程序员身上(他们应该记录它),一部分也落在客户端身上。
The technique is valid and is used all the time. However in your first example you are converting a
const char*
to a temporarystd::string
and attempting to return it, which is not the same as returning a const-reference to an object stored elsewhere. In the second example you are doing the same thing, but you are using the result before the temporary is destroyed, which in this case is legal but dangerous (see your first case.)Update: Allow me to clarify my answer some. I'm saying the problem lies in the creation of the temporary and not correctly handling the lifetimes of the objects being created. The technique is a good one, but it (along with many other good techniques) requires the pre- and post-conditions of the functions be met. Part of this burden falls on the function programmer (who should document it) and partly on the client as well.
我认为这是C++的一个小弱点。不幸的是,有两个因素组合在一起:
我对那些没有考虑他们所指向的对象的生命周期的人没有同情心。但是隐式转换确实是一个具有微妙优点和缺点的语言特性,但这里的分析并不容易。有时隐式转换是个坏消息,这就是为什么存在
explicit
关键字的原因。但问题不在于转换为string
一般而言是不好的,只是以这种不正确的方式使用这个函数是不好的。该函数的作者实际上可以通过定义重载来禁用隐式转换:
仅此更改就意味着以前不好的代码可以工作。所以我认为在这种情况下这样做是个好主意。当然,如果有人定义了一个
fun
的作者从未听说过的类型,并且它有一个运算符 std::string()
,那也是没有帮助的。此外,fun
不是一个现实的函数,对于更有用的例程,您可能不想提供对char*
进行操作的等效函数。在这种情况下,void fun(const char *);
至少强制调用者显式转换为字符串,这可能有助于他们正确使用该函数。或者,调用者可能会注意到他正在提供一个
char*
,并返回对string
的引用。在我看来,这似乎是一顿免费的午餐,所以应该敲响警钟,这根弦从哪里来,它会持续多久。I think it is a slight weakness of C++. There's an unfortunate combination of two factors:
I have no sympathy for people who fail to think about the lifetime of objects they have pointers/references to. But the implicit conversion, which certainly is a language feature with subtle pros and cons, is not making the analysis very easy here. Sometimes implicit conversion is bad news, which why the
explicit
keyword exists. But the problem isn't that conversion tostring
is bad in general, it's just bad for this function, used in this incorrect way.The author of the function can in effect disable implicit conversion, by defining an overload:
That change alone means the code which previously was bad, works. So I think it's a good idea in this case to do that. Of course it doesn't help if someone defines a type which the author of
fun
has never heard of, and which has anoperator std::string()
. Also,fun
is not a realistic function, and for more useful routines you might not want to provide an equivalent which operates onchar*
. In that case,void fun(const char *);
at least forces the caller to explicitly cast to string, which might help them use the function correctly.Alternatively, the caller could note that he's providing a
char*
, and getting back a reference to astring
. That appears to me to be a free lunch, so alarm bells should be ringing where this string came from, and how long it's going to last.是的,我同意在某些情况下这是一个相关问题。
我会使用引用计数指针来“解决”它。
Yes, I agree that there are situations where this is a relevant problem.
I would use a reference-counted pointer to "solve" it.
我认为您是在 C++98 中自找麻烦:)
这可以通过两种方式解决。首先,您可以使用共享指针。在这种情况下,内存将由
shared_ptr
自动管理,你就完成了!但是,在大多数情况下,这是一个糟糕的解决方案。因为您实际上并没有在许多引用之间共享内存。如果您考虑一直使用堆,auto_ptr
是此问题的真正解决方案。auto_ptr
需要一点点关键改进才能真正可用,这在 C++98 中是不存在的,那就是:移动语义!更好的解决方案是通过使用 C++0x 中的右值引用来允许所有权在引用之间移动。所以,你的代码看起来像(不确定语法是否正确):
I think you are asking for trouble in C++98 :)
This can be solved in two ways. First, you could use a shared pointer. In this case, the memory would be managed automatically by the
shared_ptr
, and you are done! But, this is a bad solution in most cases. Because you are really not sharing the memory between many references.auto_ptr
is the true solution for this problem, if you consider using the heap all the time.auto_ptr
needs one little crucial improvement that is not there in C++98 to be really usable, that is : Move Semantic!A better solution is to allow ownership to be moved between references, by using r-value references, which is there in C++0x. So, your piece of code would look like(not sure if the syntax is correct):
问错问题了,真的。您所要做的就是包括返回的引用是否可能是参数(通过引用传递),并将其记录为接口的一部分。 (这通常也很明显。)让调用者决定要做什么,包括将临时对象变成显式对象,然后传递它。
记录返回的指针和引用的生命周期是常见且必需的,例如 std::string::data。
通常,您可以按值传递和返回。这通常是通过 std::copy(在本例中为目标迭代器)之类的东西来完成的。
Wrong question to ask, really. All you have to do is include whether the returned reference might be to a parameter (passed by reference), and document that as part of the interface. (This is often obvious already, too.) Let the caller decide what to do, including making the temporary into an explicit object and then passing that.
It is common and required to document the lifetimes of returned pointers and references, such as for std::string::data.
Often you can pass and return by value instead. This is commonly done with things like std::copy (for the destination iterator in this case).
在即将推出的 C++ 标准中,右值引用可用于使临时对象保持“活动”状态,并解决您遇到的问题。
您可能还想查找完美转发并移动构造函数。
In the upcoming C++ standard, r-value references can be used to keep your temporary objects 'alive' and would fix the issue that you're having.
You may want to look up perfect forwarding and move constructors as well.