Search code examples
c++stringc++98

C++98 string concatenation with c_str() return


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

Solution

  • 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
    }