My question has been pretty well answered already.
But just in case people wondered, I had the mistaken belief that a unique_ptr<Derived> could be moved to a unique_ptr<Base> and would then remember the deleter for the Derived object, i.e., that Base would not need to have a virtual destructor. That was wrong. I'd select Kerrek SB's comment as "the answer", except one cannot do that for a comment.
@Howard: the code below illustrates one way to achieve what I believed the cost of a dynamically assigned deleter had to mean that unique_ptr supported out of the box:
#include <iostream>
#include <memory>           // std::unique_ptr
#include <functional>       // function
#include <utility>          // move
#include <string>
using namespace std;
class Base
{
public:
    Base() { cout << "Base:<init>" << endl; }
    ~Base() { cout << "Base::<destroy>" << endl; }
    virtual string message() const { return "Message from Base!"; }
};
class Derived
    : public Base
{
public:
    Derived() { cout << "Derived::<init>" << endl; }
    ~Derived() { cout << "Derived::<destroy>" << endl; }
    virtual string message() const { return "Message from Derived!"; }
};
class BoundDeleter
{
private:
    typedef void (*DeleteFunc)( void* p );
    DeleteFunc  deleteFunc_;
    void*       pObject_;
    template< class Type >
    static void deleteFuncImpl( void* p )
    {
        delete static_cast< Type* >( p );
    }
public:
    template< class Type >
    BoundDeleter( Type* pObject )
        : deleteFunc_( &deleteFuncImpl< Type > )
        , pObject_( pObject )
    {}
    BoundDeleter( BoundDeleter&& other )
        : deleteFunc_( move( other.deleteFunc_ ) )
        , pObject_( move( other.pObject_ ) )
    {}
    void operator() (void*) const
    {
        deleteFunc_( pObject_ );
    }
};
template< class Type >
class SafeCleanupUniquePtr
    : protected unique_ptr< Type, BoundDeleter >
{
public:
    typedef unique_ptr< Type, BoundDeleter >    Base;
    using Base::operator->;
    using Base::operator*;
    template< class ActualType >
    SafeCleanupUniquePtr( ActualType* p )
        : Base( p, BoundDeleter( p ) )
    {}
    template< class Other >
    SafeCleanupUniquePtr( SafeCleanupUniquePtr< Other >&& other )
        : Base( move( other ) )
    {}
};
int main()
{
    SafeCleanupUniquePtr< Base >  p( new Derived );
    cout << p->message() << endl;
}