Say I have the following function:
const std::string& Cat::getKittenName() const { Kitten* kitty = getKitty(); return kitty->getName(); }
Where Kitten::getName
returns a const std::string&
how do I best handle the case where kitty
is a nullptr
? I could return std::string("")
but then I am returning a reference to a temporary and practically guaranteeing undefined behaviour. I could change the getKittenName
function to return a std::string
to get around this but then I am introducing a redundant copy for all the cases where kitty
is available. Right now I feel the best option is:
const std::string& Cat::getKittenName() const { Kitten* kitty = getKitty(); if (kitty) { return kitty->getName(); } static std::string empty(""); return empty; }
The only issue might be if 'magic statics' aren't available. Is there any problem with this solution or is there a better way to do it?
You want to return a const reference when you return a property of an object, that you want not to be modified out-side of it. For example: when your object has a name, you can make following method const std::string& get_name(){ return name; }; . Which is most optimal way.
If the thing you are returning by reference is logically part of your this object, independent of whether it is physically embedded within your this object, then a const method needs to return by const reference or by value, but not by non-const reference.
Not just a copy; it is also a const copy. So you cannot modify it, invoke any non-const members from it, or pass it as a non-const parameter to any function. If you want a modifiable copy, lose the const decl on protos .
No. A reference is simply an alias for an existing object.
You have several options, really.
The simplest one would be to return std::string
, but you mentioned you do not want that for performance reasons. I'd say you should first profile to make sure it will present a noticeable performance problem, because all other solutions will make the code more complicated and hence at least a little bit harder to maintain. But let's say it does appear to be significant.
If you're worried about thread-safe function-scope statics not being implemented, you can create the fallback value as a static member of Cat
:
class Cat { static const std::string missingKittenName; public: const std::string& Cat::getKittenName() const { Kitten* kitty = getKitty(); if (kitty) return kitty->getName(); else return missingKittenName; } };
Since Kitten::getName()
apparently returns a reference (otherwise you wouldn't be worried about copies), you could also return a pointer:
const std::string* Cat::getKittenName() const { Kitten* kitty = getKitty(); if (kitty) return &kitty->getName(); else return nullptr; }
You could return an optional reference to a string:
boost::optional<const std::string&> Cat::getKittenName() const { Kitten* kitty = getKitty(); if (kitty) return kitty->getName(); else return boost::none; }
Since C++17, optional
is part of the standard library as std::optional
, so there is no longer need to fall back on Boost.
If the fact that a name is missing is an exception circumstance (an error), you could throw an exception:
const std::string& Cat::getKittenName() const { Kitten* kitty = getKitty(); if (kitty) return kitty->getName(); else throw std::invalid_argument("Missing kitten"); }
Return a reference to a const static std::string.
Reasons:
If you're multi-threaded on a pre-c++11 compiler, then you will need to write a thread-safe singleton to manufacture the default string, or define it at file scope.
c++11:
const std::string& Cat::getKittenName() const { static const std::string noname { /* empty string */ }; Kitten* kitty = getKitty(); if (kitty) { return kitty->getName(); } return noname; }
c++03:
namespace { const std::string noname; } const std::string& Cat::getKittenName() const { Kitten* kitty = getKitty(); if (kitty) { return kitty->getName(); } return noname; }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With