4

I am a Java developer trying to solve a simple C++ task. In Java, if I want to return an empty/null object, to indicate that object is not found I just return null. Example:

Person getGetByName(String name) {

    for (int i = 0; i < 10; i++) {
        if (people[i].name == name) {
            return people[i];
        }
    }
    return null;
}

later in code I can do

Person p = getByName("Sam");

if (p == null) { ...
}

In C++ I have the following method

It seems that if I declare a method like the above, returning NULL is not an option since the method doesn't return a pointer but rather a class. So what do I return to indicate that no person is found ?

dodekja
  • 537
  • 11
  • 24
Adelin
  • 18,144
  • 26
  • 115
  • 175
  • 1
    https://stackoverflow.com/questions/1282295/what-exactly-is-nullptr . C++ has nullptr – Niteya Shah May 01 '21 at 14:55
  • 1
    Use `std::optional` https://en.cppreference.com/w/cpp/utility/optional – Richard Critten May 01 '21 at 15:01
  • 4
    You should maybe not tag the question with [tag:java]. Sure, you mention Java for context, but apart from that this is a C++ question, not a Java one. – mediocrevegetable1 May 01 '21 at 15:04
  • Java programmers have learned many things that work differently in C++. This is one of them. Java returns **references** (which in C++ are pointers) to objects; C++ returns objects. C++ doesn't have null objects. It has null pointers. – Pete Becker May 01 '21 at 15:18

4 Answers4

3

If you're using C++17 or above, use std::optional.

Alternatively, return a value that would convey a similar meaning, e.g. a std::pair<bool, Person> Note that this would still require a Person object to be there, it merely does not need to be valid. If the objects are too large to be returned emptily like that, you'll have to find a workaround, like a union, or a pointer.

IWonderWhatThisAPIDoes
  • 1,000
  • 1
  • 4
  • 14
2

There's one more problem to the above code - the C++ version is inefficient. In C++, returning Person by-value always returns a copy of it (except for RVO, which is not applicable here).

Unlike C++, Java has no value semantics for objects, in Java returning Person always returns a reference to an object. A reference in Java works more like a pointer in C++, e.g. it can be null.

So the equivalent C++ code would actually be:

Person* getGetByName(std::string const& name) {
  for(int i = 0 ;i<10 ;i++) { 
    if(people[i].name == name){
      return &people[i]; 
    }  
  }
  return nullptr;
}

Now you can do

Person* p = getGetByName("blah");
if (p == nullptr) {
  // not found ...
} else {
  // use p->name etc.
}

C++ has no automatic memory management. The lifetime of the people vector must thus outlast the returned pointer. If that's not the case then it's more idiomatic to work with smart pointers instead (e.g. unique_ptr or shared_ptr). Thinking about object lifetimes is the responsibility of the C++ developer.

rustyx
  • 80,671
  • 25
  • 200
  • 267
1

You could return the person object through a parameter and return a bool to indicate if something meaningful was returned through the parameter:

bool getGetByName(const std::string& name, Person& person)
{
  for(int i = 0; i < 10 ;i++)
  { 
    if(people[i].name == name)
    {
      person = person[i]     // Writing in the person param (note the reference in the definition of the 'person' parameter).
      return true;           // Indicate success (i.e. non null).
    }
  }  

  return false;              // Indicates null.
}

Later in code you could write:

Person aPerson;
if(getGetByName("A name", aPerson))
{
  // aPerson is valid, do something with it.
}

// aPerson is not valid, handle it.
BobMorane
  • 3,870
  • 3
  • 20
  • 42
  • C++17 has [`std::optional`](https://en.cppreference.com/w/cpp/utility/optional). `Person*` is also an option here. – Jarod42 May 03 '21 at 12:55
1

In Java, class instance variables and such returned objects are actually references/pointers. Since they are pointers, they can point to null. Primitive types such as int on the other hand are not pointers. Since they aren't pointers, they cannot point to null. You cannot have int x = null;.

In C++, class instance variables and such returned objects are not pointers. Like the Java primitives, they cannot point to null.

How to properly return null/empty object in C++?

If the type of the object has a value that represents null or empty, then simply return that value. Here is an example where that type is a pointer:

int* function()
{
    return nullptr;
}

When a type has an empty value, such value can usually (but not necessarily) be created using value initialisation. Here is an example that returns an empty vector:

std::vector<int> function()
{
    return {};
}

If the type doesn't have a representation for null or empty, then you cannot return a null or empty value that doesn't exist.

However, using type erasure techniques, it is possible to design a class that internally contains either a value of another type, or doesn't. Using a template, such class can be used to augment any type with an empty value. The standard library comes with such template: std::optional.


As for your particular example, idiomatic solution in C++ is to return an iterator to the found element, and return an iterator to the (one past the) end of the range if nothing is found. Iterator is a generalisation of a pointer. Your example re-written in C++:

auto getGetByName(std::string_view name) {
    auto first = std::begin(people);
    auto last  = std::end(people);
    for (; first != last; ++first) {
        if (*first == name) {
            return first;
        }
    }
    return last;
}

// later

auto it = getByName("Sam");
if (it == std::end(people)) { ...
}

Note that there is no need to write that function, since the C++ standard library provides implementation of linear search. Simply call:

auto it = std::ranges::find(people, "Sam");
eerorika
  • 232,697
  • 12
  • 197
  • 326