99

I'm pretty new to C++ so I tend to design with a lot of Java-isms while I'm learning. Anyway, in Java, if I had class with a 'search' method that would return an object T from a Collection< T > that matched a specific parameter, I would return that object and if the object was not found in the collection, I would return null. Then in my calling function I would just check if(tResult != null) { ... }

In C++, I'm finding out that I can't return a null value if the object doesn't exist. I just want to return an 'indicator' of type T that notifies the calling function that no object has been found. I don't want to throw an exception because it's not really an exceptional circumstance.

This is what my code looks like right now:

class Node {
    Attr& getAttribute(const string& attribute_name) const {
       //search collection
       //if found at i
            return attributes[i];
       //if not found
            return NULL; // what should this be?
    }

private:
    vector<Attr> attributes;
}

How can I change it so I can give that kind of marker?

Nic
  • 6,211
  • 10
  • 46
  • 69
aduric
  • 2,812
  • 3
  • 22
  • 16
  • 6
    Exception and NULL aren't always the only solutions. You can often pick a value to return indicating not found: for example, `std::find(first, last, value)` returns `last` if no element matches. – Cascabel Apr 14 '10 at 16:47

9 Answers9

74

In C++, references can't be null. If you want to optionally return null if nothing is found, you need to return a pointer, not a reference:

Attr *getAttribute(const string& attribute_name) const {
   //search collection
   //if found at i
        return &attributes[i];
   //if not found
        return nullptr;
}

Otherwise, if you insist on returning by reference, then you should throw an exception if the attribute isn't found.

(By the way, I'm a little worried about your method being const and returning a non-const attribute. For philosophical reasons, I'd suggest returning const Attr *. If you also may want to modify this attribute, you can overload with a non-const method returning a non-const attribute as well.)

Jesse Beder
  • 33,081
  • 21
  • 109
  • 146
  • 2
    Thanks. By the way, is this an accepted way of designing such a routine? – aduric Apr 14 '10 at 17:00
  • 7
    @aduric: Yes. References imply the result has to exist. Pointers imply the result might not exist. – Bill Apr 14 '10 at 17:15
  • 7
    Just curious, shall we return `nullptr` instead of `NULL` for c++11 now? –  Nov 09 '14 at 19:12
  • 1
    yes always use nullptr over NULL in C++11 and later. if you need to be backwards compatible with earliver versions then don't – Conrad Jones Jan 12 '18 at 20:53
57

There are several possible answers here. You want to return something that might exist. Here are some options, ranging from my least preferred to most preferred:

  • Return by reference, and signal can-not-find by exception.

    Attr& getAttribute(const string& attribute_name) const 
    {
       //search collection
       //if found at i
            return attributes[i];
       //if not found
            throw no_such_attribute_error;
    }

It's likely that not finding attributes is a normal part of execution, and hence not very exceptional. The handling for this would be noisy. A null value cannot be returned because it's undefined behaviour to have null references.

  • Return by pointer

    Attr* getAttribute(const string& attribute_name) const 
    {
       //search collection
       //if found at i
            return &attributes[i];
       //if not found
            return nullptr;
    }

It's easy to forget to check whether a result from getAttribute would be a non-NULL pointer, and is an easy source of bugs.

  • Use Boost.Optional

    boost::optional<Attr&> getAttribute(const string& attribute_name) const 
    {
       //search collection
       //if found at i
            return attributes[i];
       //if not found
            return boost::optional<Attr&>();
    }

A boost::optional signifies exactly what is going on here, and has easy methods for inspecting whether such an attribute was found.


Side note: std::optional was recently voted into C++17, so this will be a "standard" thing in the near future.

Kaz Dragon
  • 6,681
  • 2
  • 36
  • 45
  • +1 I would just mention boost::optional first, and only briefly mention the other alternatives. – Nemanja Trifunovic Apr 14 '10 at 17:02
  • Ya I saw boost::optional mentioned somewhere but I was thinking that it required too much overhead. If using it is the best approach to these kinds of problems, I will start using it. – aduric Apr 14 '10 at 17:09
  • `boost::optional` does not involve much overhead (no dynamic allocation), which is why it's so great. Using it with polymorphic values requires wrapping references or pointers. – Matthieu M. Apr 14 '10 at 18:09
  • 2
    @MatthieuM. It is likely that the overhead aduric was referring to was not performance, but the cost of including an external library into the project. – Swoogan Oct 28 '11 at 19:11
  • An addendum to my answer: do note that there is a movement afoot to standardize optional as a std component, probably for what may well be C++17. So it's worth knowing about this technique. – Kaz Dragon Feb 19 '14 at 10:19
  • A further addendum: std::optional is indeed part of C++17, but unfortunately doesn't support optional references. – Kaz Dragon Feb 27 '18 at 09:25
22

You can easily create a static object that represents a NULL return.

class Attr;
extern Attr AttrNull;

class Node { 
.... 

Attr& getAttribute(const string& attribute_name) const { 
   //search collection 
   //if found at i 
        return attributes[i]; 
   //if not found 
        return AttrNull; 
} 

bool IsNull(const Attr& test) const {
    return &test == &AttrNull;
}

 private: 
   vector<Attr> attributes; 
};

And somewhere in a source file:

static Attr AttrNull;
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
3

If you want a NULL return value you need to use pointers instead of references.

References can't themselves be NULL.

(Note to the future comment posters: Yes you can have the address of a reference be NULL if you really really try to).

See my answer here for a list of differences between references and pointers.

Community
  • 1
  • 1
Brian R. Bondy
  • 339,232
  • 124
  • 596
  • 636
3

As you have figured out that you cannot do it the way you have done in Java (or C#). Here is another suggestion, you could pass in the reference of the object as an argument and return bool value. If the result is found in your collection, you could assign it to the reference being passed and return ‘true’, otherwise return ‘false’. Please consider this code.

typedef std::map<string, Operator> OPERATORS_MAP;

bool OperatorList::tryGetOperator(string token, Operator& op)
{
    bool val = false;

    OPERATORS_MAP::iterator it = m_operators.find(token);
    if (it != m_operators.end())
    {
        op = it->second;
        val = true;
    }
    return val;
}

The function above has to find the Operator against the key 'token', if it finds the one it returns true and assign the value to parameter Operator& op.

The caller code for this routine looks like this

Operator opr;
if (OperatorList::tryGetOperator(strOperator, opr))
{
    //Do something here if true is returned.
}
A.B.
  • 1,554
  • 1
  • 14
  • 21
1

The reason that you can't return NULL here is because you've declared your return type as Attr&. The trailing & makes the return value a "reference", which is basically a guaranteed-not-to-be-null pointer to an existing object. If you want to be able to return null, change Attr& to Attr*.

JSBձոգչ
  • 40,684
  • 18
  • 101
  • 169
0

You are unable to return NULL because the return type of the function is an object reference and not a pointer.

codaddict
  • 445,704
  • 82
  • 492
  • 529
0

There is one more option that could be considered in this situation - depending on your design. You can return the value using an argument to your function and make the function return bool, e.g.

bool getAttribute(const string& attribute_name, Attr& returnAttr) const {
   //search collection
   //if found at i
        returnAttr = attributes[i];
        return true;
   //if not found
        return false;
}
Pan P
  • 84
  • 8
-3

You can try this:

return &Type();
Created
  • 87
  • 1
  • 13
  • 6
    While this code snippet may solve the question, [including an explanation](http://meta.stackexchange.com/questions/114762/explaining-entirely-‌​code-based-answers) really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. – NathanOliver Dec 14 '15 at 21:10
  • This probably return a dead reference to a object on method stack, isn't it ? – mpromonet Dec 16 '15 at 21:37