简体   繁体   中英

Slicing std::out_of_range to std::exception in Visual Studio vs g++

I noticed the following behavior by accident (missed catching by reference), but I couldn't find information that, if I knew it before hand, would have allowed me to predict it.

With the minimal example

#include <iostream>
#include <stdexcept>


int main()
{
    try
    {
        // Added this and the next line to check that the exception 
        // created has already the what() set to the input string.
        std::out_of_range d("Out of range exception"); 
        std::cout << "d.what() = " << d.what() << std::endl;
        throw d;
    }
    catch (std::exception e) // Captured by value
    {
        std::cout << e.what() << std::endl;
    }

}

If I compile it with g++ -std=c++17 and with Visual C++ I get different behaviors. With the first it prints d.what() = Out of range exception\\nstd::exception , while the second it prints d.what() = Out of range exception\\nOut of range exception .

In principle there could be slicing when the std::out_of_range is captured by value and converted to the std::exception type. This means that I could expect not getting the same behavior as an object from std::out_of_range object when printing its what() .

Question: The part that I don't know how to explain is getting different behaviors for the two compilers. Is this because this slicing is undefined behavior in the C++ standarization, or is it that one of these two compilers is not complying with it?

Extra observation: I just noticed that in this link there is no mention of the class std::exception having a constructor that inputs a const char* const & , while in the Microsoft website they include it. My accidental example shows that they indeed implemented these classes differently. My question is still whether they were allowed (if this behavior is undefined) or if one of them is not complying and which one.

The object is still being sliced; you can use typeid( e ).name() to print out the actual type and it shows as std::exception . As you found, MSVC implements what() to return a pointer to a string that is set at std::exception construction time, so it's not lost when the out_of_range exception is sliced back into the base exception.

Per https://en.cppreference.com/w/cpp/error/exception/exception , what() "returns an implementation-defined string" so MSVC is free to do it this way.

To print the type, add this to your catch block:

std::cout << "e.what() = " << e.what() << " Actual type = " << typeid( e ).name() << std::endl;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM