c++unique-ptrc++23function-call-operator

Passing a static operator() as deleter type


Is the following code snippet legal in C++23?

#include <memory>
#include <cstdio>


int main()
{
    struct custom_deleter
    {
        static void operator()(int* const ptr)
        {
            delete ptr;
            std::fputs( "Deleted\n", stdout );
        }
    };

    auto ptr { std::unique_ptr<int, decltype(&custom_deleter::operator())> {new int {5},
                                                                            custom_deleter::operator()} };
}

GCC seems to be fine with it. However, I wonder whether or not it is standard-compliant.

Also, why does removing the & from decltype(&custom_deleter::operator()) cause a compile error? Does this mean that the Deleter type template parameter has to be a function pointer type (in this case void(*)(int*))?


Solution

  • This is perfectly valid. static operator() is a new C++23 language feature, and you're using it correctly.

    However, unless you really specifically want a function pointer deleter (because... maybe you want to swap a different function pointer at some point, and you need that flexibility), you really should keep the whole type in there:

    std::unique_ptr<int, custom_deleter>
    

    This will optimize much better:


    Also, why does removing the & from decltype(&custom_deleter::operator()) cause a compile error? Does this mean that the Deleter type template parameter has to be a function pointer type?

    Because decltype(&custom_deleter::operator()) is void(*)(int*) - it's a pointer to function. But decltype(custom_deleter::operator()) is just void(int*). That's a function type.

    The Deleter doesn't have to be a function pointer type, specifically (if anything, you should really avoid using function pointer types unless you really specifically need that flexibility), but unique_ptr<T, Deleter> has a member of type Deleter, so Deleter had better be a type that you can have a non-static data member of - that works for function pointers and function objects, but not plain functions. That's why the & is necessary.


    Lastly, I just want to comment on this style:

    auto var { Type { ... } };
    

    Please just write:

    auto var = Type { ... };
    

    For declarations where you're just declaring a variable with explicit type like this, there's no difference anyway. Those two mean exactly the same thing. I have no idea why this syntax is even supported (auto x{1, 2, 3}; is invalid anyway, you have to write auto x = {1, 2, 3}; if you want a std::initializer_list<int>). Always using = means all your variable declarations look the same, including the ones initialized from function calls, etc, and saves you the extra nesting of braces which makes the rest harder to parse (for humans, the compiler is fine with it).