I wanted to create a custom exception by concatonating a string before returning it on what(). I am aware of other ways to achive the wanted result, i just want to undertand why the 3 following pieces of code are behaving differently.
class InvalidFormat : public std::exception
{
private:
std::string _exceptionValue;
public:
InvalidFormat(std::string str);
const char *what() const throw();
};
InvalidFormat::InvalidFormat(std::string str) : _exceptionValue(str) {}
const char *InvalidFormat::what() const throw() {
return ("Format is invalid => " + _exceptionValue).c_str();}
//doesn't output anything
InvalidFormat::InvalidFormat(std::string str) : _exceptionValue(str) {}
const char *InvalidFormat::what() const throw() {
return std::string("Format is invalid => ").assign(_exceptionValue).c_str();}
//outputs random data
InvalidFormat::InvalidFormat(std::string str) : _exceptionValue("Format is invalid => " + str) {}
const char *InvalidFormat::what() const throw() {
return _exceptionValue.c_str();}
//outputs is correct
The following calls c_str()
on a temporary std::string
whose life ends after the full expression (at the ;
):
return ("Format is invalid => " + _exceptionValue).c_str(); // UB-prone
When InvalidFormat::what()
returns, the returned pointer points to memory freed and gone, which is undefined behaviour. This is no better:
return std::string("Format is invalid => ").assign(_exceptionValue).c_str(); // UB-prone
You must store the result of the concatenation if you want to return a non-owning pointer to it:
InvalidFormat::InvalidFormat(std::string str)
: _exceptionValue("Format is invalid => " + str)
{}
const char *InvalidFormat::what() const throw()
{
return _exceptionValue.c_str(); // OK
}