49

In dynamically typed languages like JavaScript or PHP, I often do functions such as:

function getSomething(name) {
    if (content_[name]) return content_[name];
    return null; // doesn't exist
}

I return an object if it exists or null if not.

What would be the equivalent in C++ using references? Is there any recommended pattern in general? I saw some frameworks having an isNull() method for this purpose:

SomeResource SomeClass::getSomething(std::string name) {
    if (content_.find(name) != content_.end()) return content_[name];
    SomeResource output; // Create a "null" resource
    return output;
}

Then the caller would check the resource that way:

SomeResource r = obj.getSomething("something");
if (!r.isNull()) {
    // OK
} else {
    // NOT OK
}

However, having to implement this kind of magic method for each class seems heavy. Also it doesn't seem obvious when the internal state of the object should be set from "null" to "not null".

Is there any alternative to this pattern? I already know it can be done using pointers, but I am wondering how/if it can be done with references. Or should I give up on returning "null" objects in C++ and use some C++-specific pattern? Any suggestion on the proper way to do that would be appreciated.

jalf
  • 243,077
  • 51
  • 345
  • 550
laurent
  • 88,262
  • 77
  • 290
  • 428
  • 1
    Does "besides using pointers" exclude smart pointers? – hmjd Apr 29 '12 at 09:34
  • Returning iterators is a pattern that comes up quite often. You return an invalid iterator if the thing was not found. – Mat Apr 29 '12 at 09:39
  • Well your suggestions `isNull()` is not typical C++. What is the real problem you are trying to solve. Maybe we can provide better ideas if we understand what you are trying to do. – Martin York Apr 29 '12 at 09:41
  • 5
    Why are you effectively asking "what is the way to solve this problem, **beside the correct way of solving it**"? Why do you not want to use pointers for the use case pointers are intended to solve? – jalf Apr 29 '12 at 09:41
  • @jalf, I use the pattern quite frequently, not just to return values but also for lazy initialization. So far I've been using pointers but would like to know if there's a better way to do it using references. – laurent Apr 29 '12 at 09:44
  • Ok, the question text should mention that. :) (And the answer is "no, there is not. References in other languages have more in common with C++ pointers than with C++ references) – jalf Apr 29 '12 at 09:47
  • 1
    Edited your question to add a note about that, making it clear to readers *why* you're asking about doing it with references – jalf Apr 29 '12 at 10:58
  • I posted a few options in answer to a similar question: [this-code-appears-to-achieve-the-return-of-a-null-reference-in-c](http://stackoverflow.com/questions/2894891/this-code-appears-to-achieve-the-return-of-a-null-reference-in-c/2896330#2896330) These will give you a view of how this is commonly done in C++ – Component 10 Apr 29 '12 at 10:37

9 Answers9

55

You cannot do this during references, as they should never be NULL. There are basically three options, one using a pointer, the others using value semantics.

  1. With a pointer (note: this requires that the resource doesn't get destructed while the caller has a pointer to it; also make sure the caller knows it doesn't need to delete the object):

    SomeResource* SomeClass::getSomething(std::string name) {
        std::map<std::string, SomeResource>::iterator it = content_.find(name);
        if (it != content_.end()) 
            return &(*it);  
        return NULL;  
    }
    
  2. Using std::pair with a bool to indicate if the item is valid or not (note: requires that SomeResource has an appropriate default constructor and is not expensive to construct):

    std::pair<SomeResource, bool> SomeClass::getSomething(std::string name) {
        std::map<std::string, SomeResource>::iterator it = content_.find(name);
        if (it != content_.end()) 
            return std::make_pair(*it, true);  
        return std::make_pair(SomeResource(), false);  
    }
    
  3. Using boost::optional:

    boost::optional<SomeResource> SomeClass::getSomething(std::string name) {
        std::map<std::string, SomeResource>::iterator it = content_.find(name);
        if (it != content_.end()) 
            return *it;  
        return boost::optional<SomeResource>();  
    }
    

If you want value semantics and have the ability to use Boost, I'd recommend option three. The primary advantage of boost::optional over std::pair is that an unitialized boost::optional value doesn't construct the type its encapsulating. This means it works for types that have no default constructor and saves time/memory for types with a non-trivial default constructor.

I also modified your example so you're not searching the map twice (by reusing the iterator).

Sven
  • 21,903
  • 4
  • 56
  • 63
  • +1: Of course it's not too difficult to roll your own "optional" class if a boost dependence is not desired. – Troubadour Apr 29 '12 at 10:06
  • 2
    @Troubadour: It's actually surprisingly difficult to roll your own "optional" that can hold any type; you need to embed a POD object with the correct size and alignment for the type, which is not at all straightforward. (Of course it's much easier if you restrict it to default-constructible types, in which case it's essentially the same as option 2). – Mike Seymour Apr 29 '12 at 11:26
  • 1
    Nice answer. In C++11, std::unique_ptr or std::shared_ptr could be returned also. The former would correspond to value semantics in that the resource would have to be copied, while the latter would correspond to option 1 (but probably requires refactoring SomeClass's content_ to contain shared_ptrs also). Anyway, in both cases memory management is much clearer than in option 1. – Reunanen Feb 22 '15 at 16:05
28

Why "besides using pointers"? Using pointers is the way you do it in C++. Unless you define some "optional" type which has something like the isNull() function you mentioned. (or use an existing one, like boost::optional)

References are designed, and guaranteed, to never be null. Asking "so how do I make them null" is nonsensical. You use pointers when you need a "nullable reference".

jalf
  • 243,077
  • 51
  • 345
  • 550
  • Re "References are designed, and guaranteed, to *never be null*." I've yet to come across an implementation that guarantees that references aren't null. The standard does not guarantee that references are never null. This is a mandate, not a guarantee. The burden lies with the programmer. It is unfortunately quite easy (and highly UB) to make a null reference. BUT +1. If you want a "reference" that might be null, the thing to do is to use a pointer rather than a reference. – David Hammen Apr 29 '12 at 12:17
  • 1
    @DavidHammen: the standard says pretty much what I did above: that references point to an object. In doing so, the standard guarantees that references point to an object in valid well-defined C++ code. Sure, you *can* trick the compiler into creating a "null reference", but then the program is no longer bound by the behavior specified by the standard, and the guarantee does not apply. Depends on your point of view, really. :) The standard doesn't guarantee that references are non-null in programs exhibiting UB. But it does guarantee that they will be non-null in well-defined programs. :) – jalf Apr 29 '12 at 12:23
  • @jalf: We are quibbling over semantics, but I still like the word "mandate" over "guarantee". To me, "guarantee" puts the burden on the compiler vendor, while "mandate" puts the burden on the programmer. – David Hammen Apr 29 '12 at 12:49
  • 2
    @jalf: It's not exactly difficult to trick the compiler into creating a NULL reference (`int &n = *static_cast(NULL);`). I would however never suggest that's a good idea. :) – Sven Apr 29 '12 at 13:00
  • @DavidHammen In a way, you're right. But there are really two ways to look at it. There's nothing stopping the programmer from trying to define a "null reference", and the standard doesn't *forbid* it. It doesn't *mandate* anything. It just says that "if you do this, I won't vouch for the behavior of your program". In other words, "in any program that respects my rules, I guarantee that references won't be null". But as you say, it's fairly pointless quibbling. – jalf Apr 29 '12 at 13:02
  • We both agree on what matters: a "null reference" is UB, and while nothing stops you from creating one, doing so means that neither the programmer nor the compiler can assume anything about the program's behavior. And that also answers @Sven's comment: I never said it was *difficult* to trick the compiler into doing this. I'm just saying that if you do it, **everything** in your program may break. You can no longer rely on *any* of your code doing what you think. As far as the C++ standard goes, your *entire* program's behavior is now undefined. All of it. – jalf Apr 29 '12 at 13:03
7

One nice and relatively non-intrusive approach, which avoids the problem if implementing special methods for all types, is that used with boost.optional. It is essentially a template wrapper which allows you to check whether the value held is "valid" or not.

BTW I think this is well explained in the docs, but beware of boost::optional of bool, this is a construction which is hard to interpret.

Edit: The question asks about "NULL reference", but the code snippet has a function that returns by value. If that function indeed returned a reference:

const someResource& getSomething(const std::string& name) const ; // and possibly non-const version

then the function would only make sense if the someResource being referred to had a lifetime at least as long as that of the object returning the reference (otherwise you woul dhave a dangling reference). In this case, it seems perfectly fine to return a pointer:

const someResource* getSomething(const std::string& name) const; // and possibly non-const version

but you have to make it absolutely clear that the caller does not take ownership of the pointer and should not attempt to delete it.

juanchopanza
  • 223,364
  • 34
  • 402
  • 480
  • I quite like this approach, I'll have a look at it. – laurent Apr 29 '12 at 09:45
  • IMO a little complicated - use pointers like @jalf says, no need for extra libraries and perfectly fine c++. optional requires value semantics, ie what's returned is a copy (I've returned by reference in the past, but don't know if it's ub!) – Nim Apr 29 '12 at 09:53
  • @Nim I agree, it is probably simpler in the context of the question's title. The question's body suggests OP also needs a means to check validity of returned values. Plus when returning pointers one has to make absolutely certain that the ownership is well defined. – juanchopanza Apr 29 '12 at 10:02
  • Does boost.optional have many advantages over using unique_ptr and shared_ptr? They can both be tested for nullptr to see if they're invalid. – Benj Apr 29 '12 at 10:14
  • @Benj different use cases. `unique_ptr` and `shared_ptr` will eventually try to delete the pointer. So whatever they point to must be something it makes sense to delete. You couldn't use them with plain data members for example. – juanchopanza Apr 29 '12 at 10:23
  • @juanchopanza If the OP wanted to return references to POD members, he wouldn't have to worry about them being null presumably since they couldn't be. – Benj Apr 29 '12 at 10:26
  • I take your point though, for cases where the data member could be invalid but not uninitialized and you don't want to use a special value to indicate invalidity, this could be useful. – Benj Apr 29 '12 at 10:28
  • 1
    @Benj exactly, the issue is how to let the caller know whether a value is valid or not. – juanchopanza Apr 29 '12 at 10:29
5

I can think of a few ways to handle this:

  • As others suggested, use boost::optional
  • Make the object have a state that indicates it is not valid (Yuk!)
  • Use pointer instead of reference
  • Have a special instance of the class that is the null object
  • Throw an exception to indicate failure (not always applicable)
vhallac
  • 13,301
  • 3
  • 25
  • 36
  • 7
    An explanation to go along with the downvote would be nice. Especially for something that has multiple suggestions. :) – vhallac Apr 29 '12 at 09:59
  • 1
    I was going to suggest: Have a special instance of the class that is the null object. A bit like how C# does it with String.Empty. Just have a single static instance of the 'null' version of each class. Then you can compare the returned value with the 'null' instance. – Neil Apr 30 '12 at 09:13
  • @Neil Yes, it can be a handy approach in certain situations. Personally, I dislike it: once you use it, your code cannot assume (trivially) that the object you have in hand is valid. This is true for my second suggestion above as well. – vhallac Apr 30 '12 at 09:22
  • Finally someone to recommend a null object design pattern. That should really be number one. – Jakub Zaverka Nov 25 '15 at 15:38
  • I though about "Have a special instance of the class that is the null object" then came across this. – Programmer May 23 '17 at 21:16
4

unlike Java and C# in C++ reference object can't be null.
so I would advice 2 methods I use in this case.

1 - instead of reference use a type which have a null such as std::shared_ptr

2 - get the reference as a out-parameter and return Boolean for success.

bool SomeClass::getSomething(std::string name, SomeResource& outParam) {
    if (content_.find(name) != content_.end()) 
    {
        outParam = content_[name];
        return true;
    }
    return false;
}
Roee Gavirel
  • 18,955
  • 12
  • 67
  • 94
  • `shared_ptr` can only be used if the pointee can outlive the object that created it. – juanchopanza Apr 29 '12 at 10:26
  • In this case the outParam isn't constant, allowing the user to modify it for everyone, so it has a different functionality than passing a const reference. – lil' wing Dec 07 '20 at 17:14
2

This code below demonstrates how to return "invalid" references; it is just a different way of using pointers (the conventional method).

Not recommended that you use this in code that will be used by others, since the expectation is that functions that return references always return valid references.

#include <iostream>
#include <cstddef>

#define Nothing(Type) *(Type*)nullptr
//#define Nothing(Type) *(Type*)0

struct A { int i; };
struct B
{
    A a[5];
    B() { for (int i=0;i<5;i++) a[i].i=i+1; }
    A& GetA(int n)
    {
        if ((n>=0)&&(n<5)) return a[n];
        else return Nothing(A);
    }
};

int main()
{
    B b;
    for (int i=3;i<7;i++)
    {
        A &ra=b.GetA(i);
        if (!&ra) std::cout << i << ": ra=nothing\n";
        else std::cout << i << ": ra=" << ra.i << "\n";
    }
    return 0;
}

The macro Nothing(Type) returns a value, in this case that represented by nullptr - you can as well use 0, to which the reference's address is set. This address can now be checked as-if you have been using pointers.

slashmais
  • 7,069
  • 9
  • 54
  • 80
  • On Xcode 6.3 this will never work: if (!&ra), the address to a reference will always evaluate to false. – kakyo Jun 17 '15 at 23:56
2

From C++17 on, you can use the native std::optional (here) in the following way:

std::optional<SomeResource> SomeClass::getSomething(std::string name) {
    if (content_.find(name) != content_.end()) return content_[name];
    return std::nullopt;
}
1

Here are a couple of ideas:

Alternative 1:

class Nullable
{
private:
    bool m_bIsNull;

protected:
    Nullable(bool bIsNull) : m_bIsNull(bIsNull) {}
    void setNull(bool bIsNull) { m_bIsNull = bIsNull; }

public:
    bool isNull();
};

class SomeResource : public Nullable
{
public:
    SomeResource() : Nullable(true) {}
    SomeResource(...) : Nullable(false) { ... }

    ...
};

Alternative 2:

template<class T>
struct Nullable<T>
{
    Nullable(const T& value_) : value(value_), isNull(false) {}
    Nullable() : isNull(true) {}

    T value;
    bool isNull;
};
Andrew Tomazos
  • 66,139
  • 40
  • 186
  • 319
0

Yet another option - one that I have used from time to time for when you don't really want a "null" object returned but instead an "empty/invalid" object will do:

// List of things
std::vector<some_struct> list_of_things;
// An emtpy / invalid instance of some_struct
some_struct empty_struct{"invalid"};

const some_struct &get_thing(int index)
{
    // If the index is valid then return the ref to the item index'ed
    if (index <= list_of_things.size())
    {
        return list_of_things[index];
    }

    // Index is out of range, return a reference to the invalid/empty instance
    return empty_struct; // doesn't exist
}

Its quite simple and (depending on what you are doing with it at the other end) can avoid the need to do null pointer checks on the other side. For example if you are generating some lists of thing, e.g:

for (const auto &sub_item : get_thing(2).sub_list())
{
    // If the returned item from get_thing is the empty one then the sub list will
    // be empty - no need to bother with nullptr checks etc... (in this case)
}
code_fodder
  • 15,263
  • 17
  • 90
  • 167