Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

template class derived from `const` specialized version

Tags:

c++

I've stumbled upon this code (simplified version here):

    template<typename T>
    class SmartPtr;

    template<typename T>
    struct SmartPtr<const T>
    {
        const T& operator*() const { return *_ptr; }
        const T* operator->() const { return _ptr; }
        const T* _ptr;
        // more member data and functions here
        // ...
    };

    template<typename T>
    struct SmartPtr : public SmartPtr<const T>
    {
        T& operator*() const { return *const_cast<T*>(_ptr); }
        T* operator->() const { return const_cast<T*>(_ptr); }
    };

It's the first time I see a template class derived like this. Can anyone help me understand what one can achieve using this? To me it seems the general version behaves the same as the specialization if instantiated with a const T and the specialization is not necessary. Am I missing something?

like image 459
Mircea Ispas Avatar asked Nov 30 '21 13:11

Mircea Ispas


2 Answers

The general behaviour of SmartPtr<T> and SmartPtr<const T> is indeed identical and no specialization would have been needed simply to handle const and non-const versions of T.

However, the answer to your question lies in that this construction allows for implicit conversion from SmartPtr<T> to SmartPtr<const T>.

For instance, a function defined for SmartPtr<const foo> can be called with a SmartPtr<foo> as well, and thus you don't need two overloads to handle both cases:

void HandleFoo(SmartPtr<foo> ptr);
void HandleConstFoo(SmartPtr<const foo> ptr);
void dotask()
{
    SmartPtr<foo> fooptr = new foo ...;
    ...
    HandleFoo(fooptr);
    HandleConstFoo(fooptr); // implicit conversion
}

Simply defining a single class SmartPtr<T> does not allow for implicit conversion from SmartPtr<T> to SmartPtr<const T>.

For implicit conversion between different types you would normally add extra member functions, but in this case for the same class from non-const to const you would run into problems. A specialization with a const T base class is one of the ways to avoid those problems.

like image 59
Marc Stevens Avatar answered Nov 15 '22 00:11

Marc Stevens


To me it seems the general version behaves the same as the specialization if instantiated with a const T

If instantiated with a const T, the primary template version isn't used at all. The specialization will be used.

If instantiated with a non-const T, then the primary simply reuses the code from the specialization, by tweaking the return types to make sense for a pointer to a non-const piece of data.

It's also a way to get "expected" behavior between the SmartPtr specializations (to behave as standard raw pointers do with regard to qualification conversions). We can add converting constructors of course but not all cases are covered with this approach. Consider this

template<class U>
void frombulate(SmartPtr<const U>) {
}

Can we call frombulate with a SmartPtr<int>? In this implementation we can (because derived-to-base conversions are allowed in template argument deduction). But a converting constructor would not allow us that, since general conversions in template argument deduction may not be considered.

like image 25
StoryTeller - Unslander Monica Avatar answered Nov 15 '22 00:11

StoryTeller - Unslander Monica