I have a function returning a reference to an instance of my class "record".
record& get_record(int key) {
return lookup(key);
}
That is effective it returns a reference and not by value. Now I modify it a bit.
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.
}
}
Is it correct that returning a reference to a local variable is not a good idea? and how do I return x in such a way that it becomes a temporary variable?
This is worse than a bad idea, it is undefined behavior and result in most of the cases to a crash. This is bad (TM).
What you could do is changing the return type of get_record so it returns a smart pointer. If key is valid, it returns an observer pointer to it. Otherwise, it returns an owning smart pointer to a freshly created object:
#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) About [](record*){}: this no-op lambda given as the second argument to std::shared_ptr::shared_ptr() is invoked when the smart pointer is destroyed. It replaces the default deleter of std::shared_ptr whose behavior is to call delete on the owned pointer.
About why your design is flawed. In fact, making get_record return a reference makes it not consistent. What you want is:
if key is valid return a reference to an existing/permanant object, and
return a temporary object otherwise.
Those two are mutually exclusive, and your function doesn't make sense: what does get_record return semantically?
Is it correct that returning a reference to a local variable is not a good idea?
Yes. The local object will be destroyed when get out of the function so the returned reference is always dangled.
You might make x a static variable.
record& get_record(int key) {
if (valid(key))
return lookup(key);
else {
static record x;
x.valid=false;
return x;
}
}
Note that the returned reference will always refer to the same object, i.e. x.
If you are allowed to modify the get_record function you can change the return type to pointer to record instead of reference to record.
record* get_record( int key )
{
if( valid( key ) ) return &lookup( key );
else return nullptr;
}
However, this approach needs two guarantees:
the lookup function must return a reference to record
the record returned by lookup must still live when lookup returns (e.g. record is an element of some sort of container and lookup returns its reference)
As others already detail why returning a reference to a local is bad, ill just provide an alternative: Exceptions. Though you could write a custom exception, arguably an 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 does).
Have a look 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");
}
}
You will now, obviously, have to catch the exception in the code calling get_record, otherwise your program will still terminate.
Returning a reference in itself doesn't produce undefined behaviour, but if you attempt to modify it, then you will.
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
If you have access to C++17, you could implement it using std::optional. Note the use of std::reference_wrapper, because use of a reference in std::optional makes your program ill-formed.
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;
}
Without C++17, you could just return a pointer to your record:
record* get_record(int key) {
if (valid(key))
return &lookup(key);
else
return nullptr;
}
Or if you prefer, you can keep the reference return type, and throw an exception to indicate a missing record. Though this is my least preferred approach as it makes it easy to call get_record without wrapping in a try / catch.
record& get_record(int key) {
if (valid(key))
return &lookup(key);
else
throw std::out_of_range("Invalid record key!");
}
Related
What I've understood, the reason is that we unnecessarily call the copy constructor for a simple statement like a=b; (both are objects).
What I don't get is that in my book it's written that we should never pass an object by reference, because as soon as the function terminates, that reference ceases to exist.
So is the text written in my book wrong or am I missing something here?
Text
ref: Overloading assignment operator in C++
There's nothing wrong with returning a reference from a function.
Indeed that's how the assignment operator operator= is normally defined (with return *this; for method chaining)!
What you shouldn't do is return a reference to an object that goes out of scope, e.g.
int& undefinedBehaviourServer()
{
int ub;
return ub;
}
In this case, ub has automatic storage duration and the returned reference to it will dangle.
As soon as the function is completed, all objects declared in it are destroyed. Therefore, by returning a link from a function, you risk getting a call to a remote object. Let's see the typical example:
// don't do that!!!
std::string& get_str()
{
std::string s = "abc";
return s;
}
int main()
{
string &s = get_str();
// "abc"-string already destoyed at this moment
std::cout << s; // attempt to deleted string: undefined behavior
}
Therefore, it is dangerous to return references to local objects from the functions, because it may involve accessing a deleted object (undefined behavior). Although technically returning an object (not local) reference is possible and often used. For example:
std::string& get_s()
{
static std::string s = "abc";
return s;
}
int main()
{
std::string &s = get_s();
std::cout << s; // that's OK
}
In C++, operator-> has special semantics, in that if the returned type isn't a pointer, it will call operator-> again on that type. But, the intermediate value is kept as a temporary by the calling expression. This allows code to detect changes in the returned value:
template<class T>
class wrapper
{
// ...
T val;
struct arrow_helper
{
arrow_helper(const T& temp)
: temp(temp){}
T temp;
T* operator->() { return &temp; }
~arrow_helper() { std::cout << "modified to " << temp << '\n'; }
};
arrow_helper operator->() { return{ val }; }
//return const value to prevent mistakes
const T operator*() const { return val; }
}
and then T's members can be accessed transparently:
wrapper<Foo> f(/*...*/);
f->bar = 6;
Is there anything that could go wrong from doing this? Also, is there a way to get this effect with functions other than operator->?
EDIT: Another issue I've come across is in expressions like
f->bar = f->bar + 6;
since when the arrow_helper from the second operator-> is destructed it re-overwrites the value back to the original. My semi-elegant solution is for arrow_helper to have a T orig that is hidden, and assert(orig == *owner) in the destructor.
There is no guarantee that all changes will be caught:
Foo &x = f->bar;
x = 6; /* undetected change */
If there is no way to grab a reference to any data within T through T's interface or otherwise, I think this should be safe. If there's any way to grab such a pointer or reference, you're done and in undefined behavior as soon as someone saves off such reference and uses it later.
Can anyone see any problems with returning an object by value from a factory rather than returning a unique_ptr?
The following compiles and runs correctly for me, but i'm unsure if i've missed something about l-value and r-value reference interactions and lifetimes.
I've the following class hierarchy,
#include <iostream>
struct Reader {
virtual ~Reader() {}
virtual void get(int& val) = 0;
};
struct CinReader : public Reader {
~CinReader() { std::cout << "~CinReader()\n"; }
void get(int& val) override
{
std::cout << "CinReader::get\n";
std::cin >> val;
}
};
struct ConstantReader : public Reader {
void get(int& val) override
{
std::cout << "ConstantReader::get\n";
val = 120;
}
};
I am using the following factory to return instances,
enum class ReaderType { cin = 0, constant = 1 };
Reader&& readerFactoryObj(ReaderType type)
{
switch (type) {
case ReaderType::cin:
return std::move(CinReader());
break;
case ReaderType::constant:
return std::move(ConstantReader());
break;
default:
throw "Unknown Reader type";
}
}
and used as follows,
int main(void)
{
auto&& reader = readerFactoryObj(ReaderType::cin);
int val;
reader.get(val);
}
It works when passed to something that stores a reference to the Reader interface,
class Doubler
{
public:
Doubler(Reader& reader) : mReader(reader) { }
void doubleNum() const;
private:
Reader& mReader;
};
void Doubler::doubleNum() const
{
int val;
mReader.get(val);
std::cout << val * 2 << '\n';
}
int main(void)
{
auto&& reader = readerFactoryObj(ReaderType::cin);
Doubler d(reader);
d.doubleNum();
}
I realise reader isn't of type Reader now, but will be one of its concrete sub-classes.
Are there any problems with passing reader as Reader& and storing it?
Update:
I added a destructor to CinReader and it clearly shows me that its lifetime ends before it's used.
You're returning references to temporary objects. Undefined behavior. The compiler would likely warn you if you didn't trick it with a completely redundant move call.
There are two things you might return from factory-functions:
Pointers. Either std::unique_ptr or std::shared_ptr, not raw pointers, so ownership-semantics are explicit and RAII works.
The object itself by value. For an example, look at std::make_shared, std::allocate_shared and so on.
What you must never do is returning a reference or pointer to storage which will be cleaned up on return, like stack-variables.
The compiler might not catch you, it might seem to work (for a time), but it's undefined behavior.
In your case, returning by pointer would be the right thing, so polymorphism works.
As an aside, don't throw string-literals, throw a std::exception or derived.
Can anyone see any problems with returning an object by value from a factory rather than returning a unique_ptr?
A valid question, but your factory doesn't return by value. Its return type is a reference type, so you're returning by reference.
It works when passed to something that stores a reference to the Reader interface,
No, it only appears to work because your Reader types are stateless and so you happen to get away with using them after their lifetime has ended.
Reader&& readerFactoryObj(ReaderType type)
{
...
return std::move(CinReader());
You are creating a temporary (which is an rvalue) then using std::move to cast it to an rvalue (that's completely redundant) then returning a dangling reference to that temporary. This is really really bad. Don't do that. Ever.
If you want the efficiency and simplicity of move semantics then just make your functions return by value and say return x;, it's easier and does the right thing, unlike doing unnecessary casts and returning dangling references, which is more complicated and wrong.
In your specific case, since your factory wants to return two different types you can't return by value or you'd slice the object and only return the base part. So you need to return by pointer, and you had better return by smart pointer or children will point and laugh at you in the street.
The design problem is that factories generally return an object of unspecified type, which at least implements the desired interface (base class). However, it may (and often will) return objects of a more derived class.
Returning such an object by value would cause slicing. Returning a reference causes lifetime issues (as your code demonstrates). Returning a smart pointer fixes those lifetime issues.
Do I get a usual pointer as I pass a pointer to a reference of a variable or do i get a pointer to the reference? And what do i get as I pass a reference to a reference?
I am using the stack implementation of the standard library in a class, and i want to have some wrapper methods to prevent illegal access of the stack, but i am getting strange segfaults which i narrowed down to my getter-methods considering the stack.
Should those methods give back a clean reference/pointer to the original variable stored in the stack?
int& zwei() { return stack.top() };
and
int* eins() { return &stack.top() };
There is no such thing as a "pointer to a reference". References are aliases, and so taking the address of any of them will give a pointer to the same object:
int a;
int & b = a;
assert(&a == &b);
Your functions both return a valid result provided that the stack object is still alive in the scope of the function return.
std::stack<int> s;
int & foo() { return s.top(); } // OK, maybe
int * goo() { return &s.top(); } // ditto
int & boo() { std::stack<int> b; return b.top(); } // No! Dangling reference!
You should also check that the stack isn't empty, in which case top() is not valid.
(I should also council against calling a variable by the same name as a type, even though the type's name is std::stack.)
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
warning: returning reference to temporary
I am getting the error "returning reference to temporary" on the second line below.
class Object : public std::map <ExString, AnotherObject> const {
public:
const AnotherObject& Find (const ExString& string ) const {
Object::const_iterator it = find (string);
if (it == this->end()) { return AnotherObject() };
return ( it->second );
}
}
My class implements std::map.
I am new to C++ so I'm guessing its just a syntax error. Any help?
If your function looks like this:
AnotherObject& getAnotherObject()
{
. . .
Object::const_iterator it = find ("lang");
if (it == this->end()) { return AnotherObject() };
. . .
}
the problem is that the AnotherObject() you've returned will be destroyed as soon as the function exits, and so the caller to your function will have a reference to a bogus object.
If your function returned by value however:
AnotherObject getAnotherObject()
then a copy will be made before the original is destroyed and you'll be OK.
return AnotherObject(); creates an object which is destroyed before function exit - temporaries are destroyed at the end of the expression that contains them[*], and the expression AnotherObject() creates a temporary.
Since the function returns by reference, this means that by the caller even gets a chance to see that reference, it no longer refers to a valid object.
It would be OK if the function were to return by value, since the temporary would be copied[**].
[*] With a couple of situations that don't, but they don't help you here.
[**] Actually there's an optimization called "copy constructor elision" which means the temporary needn't be created, copied and destroyed. Instead, under certain conditions the compiler is allowed to just create the target of the copy in the same way it would have created the temporary, and not bother with the temporary at all.
You're creating a temporary value on the stack AnotherObject() and returning it right before it gets destroyed. Your function's caller would get garbage, and so it's forbidden.
Maybe you want to allocate it on the heap and return a pointer to it instead?
return new AnotherObject();
Alternatively, declare your function to return a "copy" to your object, instead of a reference like I'm assuming you are returning right now:
AnotherObject f()
{
return AnotherObject(); // return value optimization will kick in anyway!
}
The function must be declared to return a reference, and a reference has to refer to an object that will continue to exist after the function exits. Your temporary "AnotherObject()" is destructed right after the return, so that obviously won't work. If you can't change the method signature, you may need to throw an exception instead of returning an error value.
You should change the return type of your function from "AnotherObject&" to "AnotherObject" and return that object by value. Otherwise it will go just like Blindy described
You shouldn't return a reference to a temporary which is destroyed at the end of the line, nor a reference to a local which is destroyed at the end of the function.
If you want to keep the current signature, you'd have to add a static constant instance that you can return as a default.
#include <iostream>
template <class T>
class X
{
T value;
static const T default_instance;
public:
X(const T& t): value(t) {}
const T& get(bool b) const
{
return b ? value : default_instance;
}
};
template <class T>
const T X<T>::default_instance = T();
int main()
{
X<int> x(10);
std::cout << x.get(true) << ' ' << x.get(false) << '\n';
}
You may also return by value or return a pointer in which case you can return NULL.
The call to AnotherObject's constructor creates a new instance on the stack, which is immediatly destroyed when the method returns.
It is most likely not a good idea to create and return new object if the find fails. The calling code will have no way to tell if the object returned is a previously existing object present in the data structure.
If you do want to do this, then you should add the new object to the data structure and then return an iterator pointing to the new object IN THE DATA STRUCTURE.
Something like this:
if (it == this->end()) {
it = this->insert(pair<ExString, AnotherObject>( string, AnotherObject() ));
return it->second;
}
I personally think that this is a bit of a hack, but as long as you really stick to the const'ness of the returned reference you should be able to return a statically constructed instance of AnotherObject whose only "raison d'etre" is to be the "not found" return value of your function. Make it a static const private member of your class Object for example, and you should be ok as long as a default constructed instance of AnotherObject is not a valid value to be contained in an instance of Object.