Home > Net >  unique_ptr deleter trick: which compiler is correct?
unique_ptr deleter trick: which compiler is correct?

Time:10-13

I was going through this particular SO on how to save memory space for custom deleter pointer. At the bottom of the answer, it provides a custom written version in C 11.

After dozens of minutes trying to understand the code, I discover some compiler inconsistencies among the Big 3, where clang compiles, while the other two complain with compiler errors. (Live Demo)


My code is a little different from the other SO answer. Here it goes:

#include <cstdlib>
#include <iostream>
#include <memory>
#include <type_traits>

using namespace std;

template <class T, T t>
struct val {
    constexpr operator typename decay<T>::type() const noexcept {
        return t;
    }
};

using default_free = val<decltype(free), free>;

int main(void) {
    unique_ptr<void, default_free> p(malloc(42));
    cout << p.get() << endl;
    p.reset();
    cout << p.get() << endl;

    return 0;
}

Please correct me if I'm wrong here. The way I see it, the trick is to provide a constexpr function that can cast an object of type default_delete to a function pointer with the value of t (free as its instantiation).

So the question: which compiler is correct?

CodePudding user response:

It appears to be a gcc bug, and a completely different MSVC bug.

gcc cannot do this:

template <typename T, T t> struct foo {};

when instantiated with a function type. T has to be a template parameter for this bug to be triggered; template <void t(void*) noexcept> works.

template <typename T, T* t> struct foo {};

fixes the problem.

MSVC eats either of the previous constructs perfectly well, but it cannot use free as a non-type template argument. (Other standard C library functions as well.) A simple solution is to use a qualified name (either ::free or std::free, both work). Wrapping free in another function also fixes the problem. This is because MSVC apparently thinks (in this context) that ::free and std::free are different functions, and cannot disambiguate. One can reproduce this with no standard library:

void foo(void*);

namespace moo {
  using ::foo;
}

using namespace moo;

Now plain foo cannot be used as a template parameter.

  • Related