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.
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 } .
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.
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.
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.
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.
The concepts of this answer are taken from the C++ Programming language by Bjarne Stroustrup.
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:
Apart from that, there are further limitations:
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
There are programs that for practical or historical reasons cannot use exceptions (neither as error handling so even less):
throw
to catch
alternative error handling methods must be used.news
and delete
) rather than relying on some systematic scheme such as resource handles (string
s vector
s).In the above cases, traditional pre-exception methods are preferred.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With