3

i am learning C++ 11 and with the following code i got this error message:

test.cpp:11:13: error: ‘static void Base::operator delete(void*)’ is protected
 inline void Base::operator delete(void* p){
             ^
test.cpp:32:45: error: within this context
     std::unique_ptr<Derived, Deleter> p(new Derived);

Question: why i got this error? how should i solve it? I thought that the protected member should also be accessible by the member function in derived class. Or am i using the unique_ptr wrongly?

test.cpp is as following:

#include <memory>

class Base{
public:
    virtual void Release() const = 0;
protected:
    virtual ~Base() = default;
    static void operator delete(void* p);
};

inline void Base::operator delete(void* p){
    ::operator delete(p);
};

struct Deleter{
    constexpr Deleter() noexcept = default;
    void operator()(const Base* ptr) const{
        ptr->Release();
    }
};

class Derived : public Base{
public:
    void Release() const;  
};

void Derived::Release() const{
    delete this;
}

int main(){
    std::unique_ptr<Derived, Deleter> p(new Derived);
    return 0;
}
Cc Hh
  • 47
  • 2
  • Don't put the `delete` operator under `protected:` – selalerer Jan 07 '19 at 12:56
  • 1
    This doesn't have anything to do with inheritance. As the duplicate says, it's because you can only `new` a class if both its `operator new` and `operator delete` are accessible. – interjay Jan 07 '19 at 13:07
  • the call ::operator delete(p); is a call of static function. There is no object instantiated and no need to instantiate any object. So if you put it on protected scope, this call will not be possible – Ratah Jan 07 '19 at 13:11
  • @interjay: so you mean if the class has a overloaded operator delete, the compiler will always try to use this one defined in the class, but not the global one? i have this error, does it mean the dynamically allocating memory is failing? Or no matter if it fails or not, simply the compiler check if the operator delete is accessible? thx – Cc Hh Jan 07 '19 at 13:19
  • @Ratah: what do you mean not possible? – Cc Hh Jan 07 '19 at 13:20
  • 1
    The whole point of overloading `operator delete` is so that the compiler will use it instead of the global one. And as the duplicate answer says, the compiler has to have access to `operator delete` because it doesn't know if the construction will succeed. – interjay Jan 07 '19 at 14:21

1 Answers1

0

You have protected the delete operator, therefore it is only accessible from within the class itself and it's subclasses. Simply make it public by adding public: before the declaration.

#include <memory>

class Base{
public:
    virtual void Release() const = 0;
protected:
    virtual ~Base() = default;
public:  // <----- ADD THIS HERE
    static void operator delete(void* p);
};

inline void Base::operator delete(void* p){
    ::operator delete(p);
};

struct Deleter{
    constexpr Deleter() noexcept = default;
    void operator()(const Base* ptr) const{
        ptr->Release();
    }
};

class Derived : public Base{
public:
    void Release() const;
};

void Derived::Release() const{
    delete this;
}

int main(){
    std::unique_ptr<Derived, Deleter> p(new Derived);
    return 0;
}

As the answers in the linked question say, deletemust be globally accessible. Unfortunately I can't find it in the standard, but here is a quote from cppreference, which I think is the one that applies here:

If the static type of the object that is being deleted differs from its dynamic type (such as when deleting a polymorphic object through a pointer to base), and if the destructor in the static type is virtual, the single object form of delete begins lookup of the deallocation function's name starting from the point of definition of the final overrider of its virtual destructor. Regardless of which deallocation function would be executed at run time, the statically visible version of operator delete must be accessible in order to compile. In other cases, when deleting an array through a pointer to base, or when deleting through pointer to base with non-virtual destructor, the behavior is undefined.

florestan
  • 4,405
  • 2
  • 14
  • 28
  • Please explain, why `delete` operator is even invoked from a global context. Since the `Deleter`, passed to `std::unique_ptr` seems to release the memory via `public` method, which invokes `delete` internally. – Algirdas Preidžius Jan 07 '19 at 13:06
  • @florestan: as i understand the protected member function is also accessible by the public member function of the class – Cc Hh Jan 07 '19 at 13:22
  • 1
    @Cc Hh: "as i understand the protected member function is also accessible by the public member function of the class "- This is only for object instantiated. your delete operator is "static function", so you can't do this->::operator delete()!!! Normally you should call it Base::operator delete(), but it is protected, so delete call is not permitted in your case – Ratah Jan 07 '19 at 13:29
  • @Algirdas Preidžius: This has nothing to do with `unique_ptr`, the problem is the `new Derived` which requires the `delete` to be accessible. – florestan Jan 07 '19 at 13:40
  • @florestan I gathered that by reading the duplicate. My comment was written both before your edits, and before marking the question as duplicate, which merely asked for reasoning behind suggested change, since, at a glance, it wasn't obvious why it was needed. – Algirdas Preidžius Jan 07 '19 at 13:46
  • Could you please explain the downvote? – florestan Jan 07 '19 at 14:40
  • @florestan: i did an upvote :) thx – Cc Hh Jan 07 '19 at 14:52
  • @ Cc Hh thanks for that, but there was also one downvote. If the answer could be improved (I'm sure it can), it would be worth knowing why and how. – florestan Jan 07 '19 at 14:56