4

Suppose I have some incomplete type

// in foo.hh
struct Hidden;

that I want to use as element type of a std::vector. Using an union I can "defer" calls to the constructor(s) and the destructor of the std::vector to the implementation of the unions constructor(s) / destructor.

// in foo.hh
struct Public {
  union Defer {
    std::vector<Hidden> v;
    Defer();
    // add copy/move constructor if needed
    ~Defer();
  } d;
};

Now I can use Public by only including foo.hh and linking with the file(s) implementing Public::Defer::Defer() and Public::Defer::~Defer(). Only those will need access to the full definition of Hidden.

Is this legal C++? If so, since when?

Background: Question that came up in my answer to another question.

Daniel Jour
  • 15,219
  • 2
  • 27
  • 57
  • 2
    The world will be a better place if `union` is removed from the C++ language. – Sam Varshavchik Jun 21 '17 at 00:02
  • Wouldn't this result in a union member having a non-trivial copy constructor? – l'L'l Jun 21 '17 at 00:11
  • @I'L'I yes, though if you need a copying/moving, you can just add the corresponding constructor to the union. – Daniel Jour Jun 21 '17 at 00:45
  • 3
    you are definitely trying to create a mess which no one, even you in a week or two will be able to understand. Why not using templates instead, or vector of pointers? – Serge Jun 21 '17 at 00:45
  • @Serge I would never use such code for production (possibly unless cleanly encapsulated). This is mostly a language lawyer question ;) – Daniel Jour Jun 21 '17 at 00:47
  • @Sam note that the union isn't strictly necessary. Would also "work" with an `aligned_storage` (not sure if it's really named like that) – Daniel Jour Jun 21 '17 at 01:05
  • Does it compile? See also [Why can't a forward declaration be used for a std::vector?](https://stackoverflow.com/questions/37346/why-cant-a-forward-declaration-be-used-for-a-stdvector) and [Why C++ containers don't allow incomplete types?](https://stackoverflow.com/questions/18672135/why-c-containers-dont-allow-incomplete-types/18672346#18672346) – Phil1970 Jun 21 '17 at 02:08
  • As far as I know, if you use an union then you have to somehow ensure that both proper alignment and proper size are used... Although that code might works with some compilers, it might not be portable to other platforms as there might be some undefined behavior. – Phil1970 Jun 21 '17 at 02:14
  • Also see [How can an incomplete type be used as a template parameter to vector here?](https://stackoverflow.com/q/31345193/1708801) – Shafik Yaghmour Jun 21 '17 at 03:31

1 Answers1

4

Instantiating std::vector<T> with incomplete type T is undefined behavior up to C++14. In C++17 this limitation is relaxed somewhat:

[vector.overview]/3 An incomplete type T may be used when instantiating vector if the allocator satisfies the allocator completeness requirements 17.6.3.5.1. T shall be complete before any member of the resulting specialization of vector is referenced.

(Note: the default allocator std::allocator does satisfy those completeness requirements).

My reading is that with C++17, it's legal for a translation unit to include your header (the one that forward-declares Hidden and defines Public), and define a variable Public pub; - but not to actually use any members of pub.d.v. Before C++17, merely including the header would already trigger undefined behavior.

Igor Tandetnik
  • 45,980
  • 4
  • 51
  • 75