3

The STL <memory> header (MSVC implementation) contains a class called:

template <class _Ty> class _Ref_count_obj2 : public _Ref_count_base

This class has a member:

union {
    _Wrap<_Ty> _Storage;
};

where _Wrap is defined as:

template <class _Ty> 
struct _Wrap {
    _Ty _Value; // workaround for "T^ is not allowed in a union"
};

From my understanding, this code is designed to hold an object of type _Ty following its construction via the new operator. However I can't figure out why this was done; it seems like using a struct instead of a struct inside a union would work just as well.

Can anyone explain the reasoning behind this? Also, can anyone explain the comment in the _Wrap definition?

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • 2
    Making a `union` will prevent default destruction. Further, making it an *anonymous* union will move its member(s) names into the namespace of the enclosing scope. – Adrian Mole Jun 25 '20 at 17:29
  • 1
    @AdrianMole, but what is `T^`? Something from MS "managed C++"? – Evg Jun 25 '20 at 17:30
  • 1
    @Evg That's normally what `^` is in MSVC-world. However, the STL is most certainly *not* managed C++, and that is only in a comment, so it maybe just the implementer's shorthand for "reference" or some such. Dunno, really, which why I am hesitant about posting an answer. – Adrian Mole Jun 25 '20 at 17:34
  • 3
    @AdrianMole Here, `^` is indeed being used to refer to Managed C++ references. Even though STL isn't written in Managed C++, it has clients who are, and it uses the wrapper to accommodate those clients. (Plus, as you noted, the wrapper is needed even in standard C++ to deal with the case where the wrapped type is a reference.) – Raymond Chen Jun 25 '20 at 17:52

1 Answers1

5

First, embedding the _Storage member in a union will prevent default destruction of that object (which is, more than likely, a non-trivial type); this appears to be essential, as the class involved is a reference counter. (By default, unions have a deleted destructor; see, for example: Is a Union Member's Destructor Called .)

Second, using an anonymous union 'moves' the _Storage identifier into the enclosing scope, thus removing any need for X.-style notation (if the union were to be named X). From cppreference:

Members of an anonymous union are injected in the enclosing scope (and must not conflict with other names declared there).

Last, the need to wrap the union's member into a templated structure is so that the class will work with reference types, which are not allowed in unions. To check this last part out, try the following code with the commented-out lines made active:

template <class _Ty>
struct _Wrap {
    _Ty _Value; // workaround for "T^ is not allowed in a union"
};

template<class T>
class bob {
public:
    bob(T t) : uncle{t}//, aunty(t)
    {
    }
private:
    union {
        _Wrap<T> uncle;
    };
//    union {
//        T aunty;
//    };
};

int main()
{
    int i = 42;
    bob<int&> b{ i }; // Note: Template uses a REFERENCE type
    return 0;
}
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83