Search code examples
c++unique-ptrstatic-cast

why can static_cast compile casting raw pointers to smart pointers


I just stumbled into a cast from a smart pointer and wanted to check if static_cast could assert at compile time that the following is nonsensical:

int main()
{
    char foobar[4] = "Foo";
    std::unique_ptr<char[]> myptr = static_cast<decltype(myptr)>(foobar);
    myptr.reset();
    return 0;
}

What happens here is that myptr attempts to free foobar.

I am not asking what a smart pointer is or how to allocate or otherwise fix the above.

I thought this problem should be caught at compile time, since these types should be completely incompatible.

Why is this not detected at compile time?


Solution

  • The static_cast causes a call to the constructor of std::unique_ptr similar to what you can see in the following example

    #include <iostream>
    #include <memory>
    
    using std::cout;
    using std::endl;
    
    class Something {
    public:
        explicit Something(int) {
            cout << __PRETTY_FUNCTION__ << endl;
        }
    };
    
    int main() {
        auto something = static_cast<Something>(1);
        (void) something;
    }
    

    If you are wondering why the static_cast causes a call to the constructor of std::unique_ptr it can be explained with the following quote from the standard (emphasis mine)

    Static cast [expr.static.cast/4]

    An expression e can be explicitly converted to a type T using a static_cast of the form static_cast<T>(e) if the declaration T t(e); is well-formed, for some invented temporary variable t (8.5). The effect of such an explicit conversion is the same as performing the declaration and initialization and then using the temporary variable as the result of the conversion. The expression e is used as a glvalue if and only if the initialization uses it as a lvalue.

    So basically in your example the array is treated as a parameter to the constructor of unique_ptr and then the imaginary temporary is then used to initialize the variable myptr (with elision in most cases)

    The constructor that is called in your example is (2) in the following cppreference page http://en.cppreference.com/w/cpp/memory/unique_ptr/unique_ptr

    explicit unique_ptr( pointer p ) noexcept;
    

    Here you get a unique_ptr that points to the array.

    Then when you call reset() the unique_ptr attempts to delete the variable with automatic lifetime and causes undefined behavior. This however is not required to be detected by the compiler.