What? Exception Object Gets Sliced Even Though I Catch It With Reference?

This is a close to real-life example. Suppose that you want to write a function that reads image files (or streams) and throws custom exceptions depending on file type in case of failure. In order to be clearer, the example code has been kept as short as possible. So, here we only focus on JPEG file read exception case. The scenario is this:

  • readImageFile function is called with JPG file name argument of type std::string.
  • readImageFile function calls readJPEGFile function.
  • readJPGFile function throws a ReadJPEGFileException.
  • readImageFile function catches that exception with ReadFileException reference, adds some information and rethrows it.

Although we catch ReadJPEGFileException with reference to its base type (ReadFileException), why do we still get error message of base class ReadFileException?

C++ standard [expr.throw] section says:

  • Evaluating a throw-expression with an operand throws an exception (15.1); the type of the exception object is determined by removing any top-level cv-qualifiers from the static type of the operand ...
  • A throw-expression with no operand rethrows the currently handled exception (15.3). The exception is reactivated with the existing exception object; no new exception object is created.

That is, if you throw an exception using throw expression with operand, a copy of the exception object will be created using its static type. In that case, exception object will be copy initialized. Even if you catch it with reference this will not make difference since copy assignment in C++ is not polymorphic. In this case we are suffering from object slicing and catching the sliced object by reference. So, instead of rethrowing exception object explicitly, 'throw'ing it will do the job.
#include <iostream>
#include <exception>

class ReadFileException: public std::exception
{
    public:
    ReadFileException():
    m_strMsg(std::move("File read exception!"))
    {}

    virtual ~ReadFileException()
    {}

    virtual const char* what() const throw()
    {
        return m_strMsg.c_str();
    }

    virtual void setErrorMsg(std::string&& a_strMsg) throw()
    {
       m_strMsg = std::move(a_strMsg);
    }
    std::string m_strMsg;
};


class ReadJPGFileException: public ReadFileException
{
    public:
    ReadJPGFileException(const std::string&& a_strMsg):
    m_strMsg(std::move(a_strMsg))
    {}

    virtual ~ReadJPGFileException()
    {}

    virtual const char* what() const throw()
    {
        return m_strMsg.c_str();
    }

    virtual void setErrorMsg(std::string&& a_strMsg) throw()
    {
       m_strMsg = std::move(a_strMsg);
    }

    std::string m_strMsg;
};

void readJPGFile(std::string a_strFileName)
{
    try
    {
       throw std::exception();
    }
    catch(std::exception& ex)
    {
       throw ReadJPGFileException("Error reading JPG file: "+ a_strFileName);
    }
}

void readImageFile(std::string a_strFileName)
{
    try
    {
       readJPGFile(a_strFileName);
    }
    catch(ReadFileException& ex)
    {
       ex.setErrorMsg( std::string(ex.what()) +
                       "\nError in " +__PRETTY_FUNCTION__+
                       " line: "+
                       std::to_string(__LINE__));

       throw ex;
    }
}

int main(int argc, char *argv[])
{
    try
    {
       readImageFile("test.jpg");
    }
    catch(std::exception& ex)
    {
       std::cout<< ex.what()<<std::endl;
    }
}