In the code below, Base
is an abstract base class with non-virtual destructor.
Derived
, a concrete type, can only be instantiated inside the make_thing()
factory function (it's declaration and definition are visible exclusively from within the scope of make_thing()
).
My understanding is that, since std::shared_ptr<T>
stores the concrete deleter at the point of construction, it is well defined to delete Derived
via std::shared_ptr<Base>
.
This would not compile if make_thing()
returned a std::unique_ptr<Base>
and it would be undefined behavior if it returned Base*
.
While the code works as expected, I wonder whether it contains Undefined Behavior.
#include <iostream>
#include <memory>
class Base {
public:
void method() const noexcept { return this->do_method(); }
protected:
~Base() noexcept { std::cout << "~Base()\n"; }
private:
virtual void do_method() const noexcept = 0;
};
std::shared_ptr<Base> make_thing() {
class Derived final : public Base {
public:
~Derived() noexcept { std::cout << "~Derived()\n"; }
private:
void do_method() const noexcept override {
std::cout << "Derived::do_method()\n";
}
};
return std::make_shared<Derived>();
};
int main() {
// only way to instantiate Derived
auto thing = make_thing();
thing->method();
}
Sample run:
$ clang++ example.cpp -std=c++20
$ ./a.out
Derived::do_method()
~Derived()
~Base()