c++sfinaec++03

Eliminate functions from template class in C++03


I am stuck with a C++03 compiler and am trying to eliminate functions from a (Smart Pointer) template class for the type 'void'.

An MRE (all non-essential information stripped from the class):

template<typename T> struct EnableIfNotVoid { typedef T type; };
template<> struct EnableIfNotVoid<void> {};

template<typename T> class SmartPtr
{
    public:
        template<typename U = T>
        typename EnableIfNotVoid<U>::type &operator *(void) const
        {
            return *content;
        }

    private:
        T* content;
};

int main(int, char **)
{
    SmartPtr<int> works;
    SmartPtr<void> noWork;
    int& i = *works;
}

This works flawlessly when running it through a C++11 compiler (for which I wouldn't need the class to begin with), but when trying to compile it with the C++03 compiler I am stuck with, I get the error default template arguments may not be used in function templates.

Other things I've tried (and the errors the compiler is throwing my way):

Without default argument: no match for 'operator*' in '*works'

template<typename U>
typename EnableIfNotVoid<U>::type &operator *(void) const
{
    return content;
}

Without template: no type named 'type' in 'struct EnableIfNotVoid<void>'

typename EnableIfNotVoid<T>::type &operator *(void) const
{
    return content;
}

Without SFINAE (for completeness sake): forming reference to void

T &operator *(void) const
{
    return *content;
}

I'd like to avoid having to specialize the entire class (which is rather sizeable) only to 'leave out' the two small functions and would really appreciate it, if there was some smarter way to do this.


Solution

  • Approach with slightly extended specialized helper. Helper class provides return type alias which should be always valid to avoid breaking declarations. Dummy parameter delays function instantiation until operator * is actually used. Static assert from boost triggers reasonably meaningful compilation error if operator * is used in void smart pointer.

    #include <boost/static_assert.hpp>
    
    template<typename T>
    struct SmartPtrImpl
    {
        typedef T & Reference;
    
        template<typename Dummy>
        static Reference Deref(T * ptr)
        {
            return * ptr;
        }
    };
    
    template<>
    struct SmartPtrImpl<void>
    {
        typedef void Reference;
    
        template<typename Dummy>
        static Reference Deref(void *)
        {
            BOOST_STATIC_ASSERT_MSG((0 == sizeof(Dummy)), "SmartPtr<void> does not support dereference");
            return;
        }
    };
    
    template<typename T>
    class SmartPtr
    {
        public: typedef SmartPtrImpl<T> Impl;
    
        protected: T * m_ptr;
    
        public: SmartPtr(void): m_ptr() {}
    
        public: typename Impl::Reference
        operator *(void) const
        {
            return Impl::template Deref<int>(m_ptr);
        }
        // other stuff...
    };
    
    int main()
    {
        SmartPtr<int> works; *works;
        SmartPtr<void> noWork; // *noWork;
    }
    

    online compiler

    Uncommenting // *noWork; triggers static assertion:

    error: invalid application of 'sizeof' to incomplete type 'boost::STATIC_ASSERTION_FAILURE'
    23 | BOOST_STATIC_ASSERT_MSG((0 == sizeof(Dummy)), "SmartPtr does not support dereference");