Search code examples
c++iteratorwrapperreinterpret-cast

C++ :: Safely using reinterpret_cast to generate "wrapper" iterators


I have a vector containing values of a given primitive type, but I want to iterate this vector such that I can perform certain operations "as if" working with a class that wraps the value type.

The following code example compiles and produces the expected result:

#include <iostream>
#include <vector>

template<class T>
struct wrap 
{
    T x;

    void print() const
    { 
        if (x < 0)
            std::cout << " negative ";
        else if (x > 0)
            std::cout << " positive ";
        else 
            std::cout << " --null-- ";
    }

    void operator ++ ()
    {
        if (this->x <  static_cast<T>(0))
            this->x += static_cast<T>(1000);
    }
};


int main() 
{
    using vec_t = std::vector<int>;
    vec_t v;
    v.push_back(-1234);
    v.push_back( 5678);
    v.push_back(-4);
    v.push_back(0);

    // essential piece of code
    using vec_w = std::vector< wrap<int> >;
    vec_w::iterator it = reinterpret_cast<vec_w*>(&v)->begin();
    vec_w::iterator ti = reinterpret_cast<vec_w*>(&v)->end();

    while (it != ti)
    {
        it->print();
        ++(*it);
        it->print();
        std::cout << std::endl;
        ++it;
    }

    return 0;
}

Output:

 negative  negative 
 positive  positive 
 negative  positive 
 --null--  --null-- 

But is this safe to use, as long as the wrapper defines the exact same value type (and nothing else)?


Solution

  • But is this safe to use, as long as the wrapper defines the exact same value type (and nothing else)?

    No. You are breaking the strict aliasing rule.

    Why not wrap T&?

    template<class T>
    struct wrap 
    {
        T& x;
    
        void print() const
        { 
            if (x < 0)
                std::cout << " negative ";
            else if (x > 0)
                std::cout << " positive ";
            else 
                std::cout << " --null-- ";
        }
    
        void operator ++ ()
        {
            if (this->x <  static_cast<T>(0))
                this->x += static_cast<T>(1000);
        }
    };
    

    You can wrap in a loop

    int main() 
    {
        std::vector<int> v;
        v.push_back(-1234);
        v.push_back( 5678);
        v.push_back(-4);
        v.push_back(0);
    
        for (auto & i : v)
        {
            wrap<int> w { i };
            w.print();
            ++w;
            w.print();
            std::cout << std::endl;
        }
    
        return 0;
    }
    

    Or have a vector of wrapped

    int main() 
    {
        std::vector<int> v;
        v.push_back(-1234);
        v.push_back( 5678);
        v.push_back(-4);
        v.push_back(0);
    
        std::vector<wrap<int>> w { v.begin(), v.end() };
    
        for (auto & i : w)
        {
            i.print();
            ++i;
            i.print();
            std::cout << std::endl;
        }
    
        return 0;
    }