Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using throw to replace return in C++ non-void functions

In C++ functions, is it a good practice to replace return with throw? For example, I have the following code

// return indices of two numbers whose sum is equal to target
vector<int> twoSum(vector<int>& nums, int target) {
    for(int i=0; i<nums.size()-1; ++i)
        for(int j=i+1; j<nums.size(); ++j)
        {
            if(nums[i] + nums[j] == target) return vector<int>{i, j};
        }
    // return vector<int>{};
    throw "no solution";
}

The code above compiles with my GCC 7.2.

like image 315
user27490 Avatar asked Feb 15 '19 16:02

user27490


People also ask

What does non void function does not return a value mean in C?

In C++ it's undefined behaviour if execution actually reaches the end of a non- void function other than main , while in C it's only UB if the caller uses the return value. This means functions can look like they might reach the end without returning a value, but actually can't reach the closing } .

Does a void function need a return in C?

If a return value isn't required, declare the function to have void return type. If a return type isn't specified, the C compiler assumes a default return type of int . Many programmers use parentheses to enclose the expression argument of the return statement. However, C doesn't require the parentheses.

What happens if you forget the return statement in a non void function?

If control reaches the closing curly brace ( } ) of a non- void function without evaluating a return statement, using the return value of the function call is undefined behavior.

What is non void function in C?

If the function is non-void,it means it has to return something before reaching the end of function block[ _} ]. So, when we give only if and else-if statements the compiler cannot tell from that code,that any of these statements will be evaluated to true and return something.


3 Answers

In C++ functions, is it a good practice to replace return with throw?

Return is not something that can be replaced by a throw in general.

In exceptional cases where you have nothing to return, throwing an exception can be a valid way to exit the function.

Whether it is "good practice", and what case is "exceptional" are subjective. For example, for a search function such as yours, it's hardly a surprise that there might not be a solution, and I would argue that throwing would not be appropriate.

There are often other alternatives to throwing. Compare your algorithm with something like std::string::find that returns the index of the start of a substring. In case where substring does not exist, it returns a "non-value" std::string::npos. You could do the same and decide that the index -1 is returned when a result is not found. There is also a generic way to add non-value representation to a type in cases where none of the existing representations can be reserved for the purpose: std::optional.

P.S. A vector is probably not a good choice for returning a pair of numbers. std::pair might be better, or a custom class if you have good names for the numbers.

like image 184
eerorika Avatar answered Sep 27 '22 16:09

eerorika


The concepts of this answer are taken from the C++ Programming language by Bjarne Stroustrup.

SHORT ANSWER

Yes, exception-throwing can be used as returning value method. An example is the following for a binary tree search function:

void fnd(Tree∗ p, const string& s)
{
    if (s == p−>str) throw p; // found s
    if (p−>left) fnd(p−>left,s);
    if (p−>right) fnd(p−>right,s);
}


Tree∗ find(Tree∗ p, const string& s)
{
    try {
       fnd(p,s);
    }
    catch (Tree∗ q) {
        // q->str==s
        return q;
    }
    return 0;
}

However, it should be avoided because:

  • they allow you to separate error code from "ordinary code" making your program much more readable, comprehensible and manageable. If you use them as return method, this does not hold anymore.
  • there might be inefficiencies because exception implementations rely on the assumption that they are used as error-handling methods.

Apart from that, there are further limitations:

  • exceptions must be of copy-able type
  • exceptions can handle only synchronous events
  • they should be avoided in a time-critical system
  • they should be avoided in large old programs in which resource management is an ad hoc mess (free store is unsystematically managed using naked pointers, news and delete) rather than relying on some systematic scheme such as resource handles (strings vectors).

Longer answer

An exception is an object thrown to represent the occurrence of an error. It can be of any type that can be copied but it is strongly recommended to use only user-defined types specifically defined for that purpose. Exceptions allow the programmer to explicitly separate error-handling code from "ordinary code" making the program more readable.

First of all, exceptions are for managing synchronous events, not asynchronous ones. This is one first limitation.

One might think of the exception-handling mechanisms as simply another control structure, an alternative way of returning a value to a caller.

This has some charm but should be avoided because it is likely to cause confusion and inefficiencies. Stroustrup suggests:

When at all possible stick to the "exception handling is an error handling" view. When this is done code is separated into two categories: ordinary code and error handling code. This makes the code more comprehensible. Furthermore, the implementations of the exception mechanisms are optimized based on the assumption that this simple model underlies the use of the exception.

So basically using exceptions to return value should be avoided because

  • exception implementation is optimized assuming they are used for error-handling and not for returning values hence they might be inefficient for that;
  • They allow separating error code from ordinary code making the code much more readable and comprehensible. Anything that helps preserve a clear model of what is an error and how it is handled should be treasured.

There are programs that for practical or historical reasons cannot use exceptions (neither as error handling so even less):

  • A time-critical component of an embedded system where operation must be guaranteed to complete in a specified maximum time. In the absence of tools that can accurately estimate the maximum time for an exception to propagate from throw to catch alternative error handling methods must be used.
  • A large old program in which resource management is an ad hoc mess (free store is unsystematically managed using naked pointers, news and delete) rather than relying on some systematic scheme such as resource handles (strings vectors).

In the above cases, traditional pre-exception methods are preferred.

like image 37
Francesco Boi Avatar answered Sep 27 '22 18:09

Francesco Boi


return and throw have two different purposes and should not be considered interchangeable. Use return when you have a valid result to send back to the caller. On the other hand, use throw when some exceptional behavior occurs. You can get an idea of how other programmers use throw by using functions from the standard library and taking note of when they throw exceptions.

like image 40
Code-Apprentice Avatar answered Sep 27 '22 16:09

Code-Apprentice