Search code examples
c++templatescastingconstantsreinterpret-cast

Is casting std::pair<T1, T2> const& to std::pair<T1 const, T2> const& safe?


Is it safe (in theory or in practice) to reinterpret_cast a std::pair<T1, T2> const & into a std::pair<T1 const, T2> const &, assuming that the programmer hasn't intentionally done something weird like specializing std::pair<T1 const, T2>?


Solution

  • It's NOT portable to do so.

    std::pair requirements are laid out in clause 20.3. Clause 17.5.2.3 clarifies that

    Clauses 18 through 30 and Annex D do not specify the representation of classes, and intentionally omit specification of class members. An implementation may define static or non-static class members, or both, as needed to implement the semantics of the member functions specified in Clauses 18 through 30 and Annex D.

    This implies that it's legal (although incredibly unlikely) for an implementation to include a partial specialization such as:

    template<typename T1, typename T2>
    struct pair<T1, T2>
    {
        T1 first;
        T2 second;
    };
    
    template<typename T1, typename T2>
    struct pair<const T1, T2>
    {
        T2 second;
        const T1 first;
    };
    

    which are clearly not layout-compatible. Other variations including inclusion of additional non-static data members possibly before first and/or second are also allowed under the rule.


    Now, it is somewhat interesting to consider the case where the layout is known. Although Potatoswatter pointed out DR1334 which asserts that T and const T are not layout-compatible, the Standard provides enough guarantees to allow us to get most of the way anyway:

    template<typename T1, typename T2>
    struct mypair<T1, T2>
    {
        T1 first;
        T2 second;
    };
    
    mypair<int, double> pair1;
    mypair<int, double>* p1 = &pair1;
    int* p2 = reinterpret_cast<int*>(p1); // legal by 9.2p20
    const int* p3 = p2;
    mypair<const int, double>* p4 = reinterpret_cast<mypair<const int, double>*>(p3); // again 9.2p20
    

    However this doesn't work on std::pair as we can't apply 9.2p20 without knowing that first is actually the initial member, which is not specified.