Consider the function below which returns the kth element in a list. How do I return, at least, something from the function when the kth element is NOT found in the list? When I ran the program of which the function is a part of, the message in the else block was indeed output to the display, but, the program execution froze as subsequent statements in the program after the function call could not be executed.
template<class elemType>
const elemType& linkedListType<elemType>::kthElement(int k)
{
int i = 0;
if (k <= count)
{
nodeType<elemType>* current;
current = first;
while (i < k)
{
current = current->link;
i++;
}
return current->info;
}
else
cout<<"List does not have "<<k<<" elements!"<<endl;
}
A function that returns a reference must always return something valid. If this is not possible because the preconditions of the function have not been met by the caller, aborting, throwing, or causing undefined behavior are all acceptable. The latter is what std::vector::operator[]() does.
If a function may, for reasonable inputs, be unable to return something valid then the function should not return a reference at all.
There's a myriad of solutions for the problem of empty return value. A few are:
* The function returns an iterator for a sequence and returns an iterator to one past the end of the sequence to indicate empty.
* The function returns a pointer and nullptr to empty.
* The function returns a smart pointer and a default-constructed smart pointer for empty.
* The function returns a signed type but implements an algorithm whose domain is in the positives, so returns a negative for empty.
* The function returns a bool or some other type for signalling, and writes the real output to a reference or pointer passed as a parameter, setting aside one or more values of the return type to indicate failure. This is often used in OS APIs.
> return a boost::optional/std::experimental::optional/std::optional.
For a function that needs to return an lvalue, std::experimental::optional is not an option.
Repeat:
Please try to understand the difference between value semantics and reference semantics.
std::experimental::optional<T> holds an optional (non-polymorphic) value of type T.
"A program that necessitates the instantiation of template optional for an lvalue reference or rvalue reference type ... is ill-formed."
boost::optional<> has partial support for optional references: lvalue references are allowed, rvalue references are not. (At the time it was designed, support was complete: back then, there were no rvalue references; there was no generic code using universal references)
In conjunction with std::experimental::optional<>, if optional lvalue references are, for some reason unavoidable (raw pointers are 'evil'; the keyword this should never be used etc.), an optional wrapped reference could be used: std::experimental::optional< std::reference_wrapper<T> >
This boost stuff is way over my head, guys! I am teaching myself C++. I checked the Reference section of this website, but I couldn't find it among the header files there. Can anyone, in plain language, just explain what it is all about? Or better still, supply links to helpful/instructive literature on the concept.
@ JLBorges:
Thanks for your example codes but I find it hard to see how it resolves/deals with the problem I described earlier. Probably, it's due to my limited(/lack of) knowledge about boost. I tried throwing an exception when the kth element is not in the list; although it worked, but it halted the execution of subsequent statements in the program. I don't want that! Or am I asking for too much here and the only way out is to redefine/restructure the function??
This boost stuff is way over my head, guys! I am teaching myself C++. I checked the Reference section of this website, but I couldn't find it among the header files there. Can anyone, in plain language, just explain what it is all about?
Boost is a popular library for C++ and contains many, many sub-libraries that all do different things. Boost.Optional is just one of those sub-libraries: http://www.boost.org/doc/libs/1_59_0/libs/optional/doc/html/index.html
Parts of Boost are slowly being pulled into the C++ Standard Library, so in the future we will have std::optional and you won't need to use Boost for that particular thing.
geeloso wrote:
I tried throwing an exception when the kth element is not in the list; although it worked, but it halted the execution of subsequent statements in the program. I don't want that!
You want to eventually catch the exception.
You only throw exceptions where you don't know how to recover from an error.
You only catch exceptions where you do know how to recover from an error.
Thx LB for your quick reply and succinct explanation on boost. Regarding the exception, yeah, I had the exception thrown and caught, and yet, the program still halted the way I already described. I had the try and catch blocks both in the function as shown below, alongside a derived class from the std::exception base class:
class outOfRangeException: public exception
{
virtualconstchar* what() constthrow()
{
return"The List does not have that many elements!";
}
} error;
template<class elemType>
const elemType& linkedListType<elemType>::kthElement(int k)
{
int i = 0;
try
{
if (k <= count)
{
nodeType<elemType>* current;
current = first;
while (i < k)
{
current = current->link;
i++;
}
return current->info;
}
else
{
throw error;
}
}
catch (exception& e)
{
cout<<e.what()<<endl<<endl;
}
}
> I find it hard to see how it resolves/deals with the problem I described earlier.
It was not intended to be a solution to your earlier problem; I was merely pointing out that returning an optional value wrapped in std::experimental::optional<> is an inappropriate solution to the problem.
Throwing an exception is a sane solution; the one hat I would recommend.
Thx MiiNiPaa! For some reason, your suggestion resolved the problem. However, I am still skeptical whether the problem was solved based solely on this statement you made:
You want to let exception to escape current scope and catch it on the caller side:
At first glance, it would seem as if the problem was resolved based on this since, now, an exception is thrown in function const elemType& linkedListType<elemType>::kthElement(int k) and caught in function main(). But let me say that I have seen some examples where an exception is thrown and caught in the same function/scope (e.g. in main()).
How do you reconcile these apparent inconsistencies?
An exception should never be thrown and caught in the same scope, that is bad design and pointless. You may have seen it in examples demonstrating exception handling - remember that examples of a particular concept are often not examples of good practice ;)