tags:

views:

183

answers:

8

So I'm having a rather tumultuous conversion to C++ from Java/C#. Even though I feel like I understand most of the basics, there are some big fat gaping holes in my understanding.

For instance, consider the following function:

Fruit&
FruitBasket::getFruitByName(std::string fruitName)
{
    std::map<std::string,Fruit>::iterator it = _fruitInTheBascit.find(fruitName);
    if(it != _fruitInTheBascit.end()) 
    {
        return (*it).second;
    }
    else
    {
           //I would so love to just return null here
    }

}

Where _fruitsInTheBascit is a std::map<std::string,Fruit>. If I query getFruitByName("kumquat") you know it's not going to be there - who eats kumquats? But I don't want my program to crash. What should be done in these cases?

P.S. tell me of any other stupidity that I haven't already identified.

+1  A: 

If you need NULL, you can return a pointer instead of a reference.

dan04
+10  A: 

There is no such thing in C++ as a null reference, so if the function returns a reference, you can't return null. You have several options:

  1. Change the return type so that the function returns a pointer; return null if the element is not found.

  2. Keep the reference return type but have some sort of "sentinel" fruit object and a return a reference to it if the object is not found.

  3. Keep the reference return type and throw an exception (e.g., FruitNotFoundException) if the fruit is not found in the map.

I tend to use (1) if a failure is likely and (3) if a failure is unlikely, where "likely" is a completely subjective measure. I think (2) is a bit of a hack, but I've seen it used neatly in some circumstances.

As an example of an "unlikely" failure: in my current project, I have a class that manages objects and has a function is_object_present that returns whether an object is present and a function get_object that returns the object. I always expect that a caller will have verified the existence of an object by calling is_object_present before calling get_object, so a failure in this case is quite unlikely.

James McNellis
210
@210: True, but I generally avoid out parameters like the plague; they lower the number of places I can write `const` in my code :-). If the type is default constructible, you could return a `pair<bool, Fruit>`, which I've done on occasion, but I don't think that's as clean as other options.
James McNellis
@210: This also means that you need to copy the fruit into the output parameter. If Fruit is rather large then copying may be very expensive. But otherwise I agree with James its just not very elegant.
Martin York
You can return a null reference: `return *static_cast<Fruit*>(NULL);`. Not saying it's right or will work as expected (and it defeats the purpose), but you can surely return a null reference.
Nathan Ernst
@Nathan: In any well-defined program you cannot have a null reference, because the only way to obtain one is to dereference a null pointer (like you show in your comment). Dereferencing a null pointer results in undefined behavior.
James McNellis
+1  A: 

The reason this doesn't work is because your function returns a reference. Reference must always be actual instances. Java is not C++.

One way you could fix this is to change the function to return a pointer, which work much more like the references java uses. In that case, you can just return null;.

Fruit*
FruitBasket::getFruitByName(std::string fruitName)
{
    std::map<std::string,Fruit>::iterator it = _fruitInTheBascit.find(fruitName);
    if(it != _fruitInTheBascit.end()) 
    {
        return &(*it).second;
    }
    else
    {
           return NULL;
    }

}

If you'd like to avoid doing that, for some reason, you could define a sentinel object and return that instead. something like this

Fruit NullFruit;

Fruit&
FruitBasket::getFruitByName(std::string fruitName)
{
    std::map<std::string,Fruit>::iterator it = _fruitInTheBascit.find(fruitName);
    if(it != _fruitInTheBascit.end()) 
    {
        return (*it).second;
    }
    else
    {
        return NullFruit;
    }

}

an additional option is to not return at all. Raise an exception

class NullFruitException: public std::exception {};

Fruit&
FruitBasket::getFruitByName(std::string fruitName)
{
    std::map<std::string,Fruit>::iterator it = _fruitInTheBascit.find(fruitName);
    if(it != _fruitInTheBascit.end()) 
    {
        return (*it).second;
    }
    else
    {
        throw NullFruitException;
    }

}
TokenMacGuy
+1  A: 

References cannot be null. They work best with exceptions - instead of returning an error code, you can throw.

Alternatively, you can use an "out" parameter, with an error-code return value:

bool FruitBasket::getFruitByName(const std::string& fruitName, Fruit& fruit)
{
    std::map<std::string,Fruit>::iterator it = _fruitInTheBascit.find(fruitName);
    if(it != _fruitInTheBascit.end()) 
    {
        fruit = (*it).second;
        return true;
    }
    else
    {
        return false;
    }
}

Then call it like this:

Fruit fruit;
bool exists = basket.getFruitByName("apple", fruit);
if(exists)
{
    // use fruit
}
A: 

Hi, i'm a bit rusty in the C++ department. But is it possible to return Fruit* instead of Fruit& ?

If you make this change then you can say "return null;" (or return NULL, or return 0... whatever the syntax is for C++).

AlvinfromDiaspar
+2  A: 

OK. Lots of solutions.
James McNellis has covered all the obvious ones.
Personally I prefer his solution (1) but there are a lot of details missing.

An alternative (and I throw it out just as an alternative) is to create a Fruit reference type that knows if the object is valid. Then you can return this from your getFruitByName() method:

Basically it is the same as returning a pointer; BUT there is no ownership symantics associated with a pointer and thus it is hard to tell if you are supposed to delete the pointer. By using the fruit reference type you are not exposing the pointer so it leads to no confusion about the ownership.

class FruitReference
{
    public:
        FruitReference()  // When nothing was found use this.
            :data(NULL)
        {}
        FruitReference(Fruit& fruit)  // When you fidn data.
            :data(&fruit)
        {}
        bool   isValid() const { return data != NULL;}
        Fruit& getRef()  const { return *data; }
    private:
        Fruit*   data; //(not owned)
};

FruitReference const& FruitBasket::getFruitByName(std::string fruitName)   
{   
  std::map<std::string,Fruit>::iterator it = _fruitInTheBascit.find(fruitName);   
  if(it != _fruitInTheBascit.end())    
  {   
    return FruitReference((*it).second);   
  }   
  else   
  {   
    return FruitReference();
  }
}

I am sure boost has somthing similar but I could not find it in my 20 second search.

Martin York
It feels like a long way of just using a pointer :/ Maybe `boost::ref` would be a better "smart reference", because it very much tries to act like one.
GMan
Oh, good one. You might still want to throw an exception if you call `getRef` when the data is null. I guess it depends how much you want to trust the caller. (Funny thing... I just suggested something almost exactly the same [in response to another question](http://stackoverflow.com/questions/3284720/non-owning-holder-with-assignment-semantics/3284770#3284770)).
James McNellis
@GMan: I knew boost had somthing. But google "boost reference type" gave me lots of link to the boost reference documentation (not quite what I wanted). So use boost::ref much more tried and tested.
Martin York
@Martin: Ah, yeah. +1 for this concept though. @James: Heh, funny. I was going to suggest `boost::ref` in that question as well.
GMan
@GMan: But `boost::reference_wrapper` isn't nullable, so what would it give us here?
Georg Fritzsche
@Georg: Oh, derp, you're right! I've been thinking of `boost::ref` all day when I really meant [`boost::optional`](http://www.boost.org/doc/libs/1_42_0/libs/optional/doc/html/index.html). Yeesh.
GMan
A: 

James McNellis' answer hits it spot-on. I would point out, however, that you should be thinking of C++'s pointers as being like Java's references rather than C++'s references being like Java's references. What you'd do in your situation is similar to what you'd do in Java if you were trying to return a primitive type which can't be null. At that point, you're basically doing what James suggested:

  1. Make it pointer (for a primitive in Java, that would mean using a wrapper class such as Integer or Float, but here you would just use a pointer). However, beware of the fact that you can't return pointers to variables on the stack unless you want big trouble, since the memory will go away when the function call has completed.

  2. Throw an exception.

  3. Create an ugly sentinel value to return (which I would almost always argue is a bad idea).

There are likely other solutions, but those are the key ones, and James did a good job covering them. However, I do feel the need to point out that if you think of objects on the stack in a manner similar to primitives in Java, then it will be easier for you to figure out how to deal with them. And it's good to remember that C++'s references and Java's references are two entirely different beasts.

Jonathan M Davis
A: 

An object that can be null or invalid is sometimes caused a Fallible object. One example implementation I created can be found here: Fallible.h. Another example would be boost::optional.

StackedCrooked