10

In trying to learn how to use std::aligned_union I am having trouble finding any examples. My attempt is running into problems that I do not know how to solve.

struct include
{
    std::string file;
};
struct use
{
    use(const std::string &from, const std::string &to) : from{ from }, to{ to }
    {
    }
    std::string from;
    std::string to;
};
std::aligned_union<sizeof(use), include, use>::type item;
*reinterpret_cast<use*>(&item_) = use{ from, to };

When I attempt to run the program in VC++2013 debug mode I get a runtime error in memcpy(unsigned char * dst, unsigned char * src, unsigned long count). I assume that this is how VC++ implements the assignment from the temporary.

How would I change this so that I do not have this issue?

Graznarak
  • 3,626
  • 4
  • 28
  • 47

3 Answers3

11

The aligned_union type gives you a POD type that's suitable as storage for the desired classes - it is not actually an object of that type. You still have to construct your own object:

#include <memory>

{
    std::aligned_union<sizeof(use), include, use>::type storage;

    use * p = new (static_cast<void*>(std::addressof(storage))) use(from, to);

    // ...

    p->~use();
}
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • Side note, if I understand correctly, the main advantage of std::aligned_union over a normal union (if you want to write variant-like objects) is that the default constructor and destructor are available by default (and does not do anything) instead of [being deleted](https://stackoverflow.com/questions/19764150/questions-regarding-c-non-pod-unions). – user202729 Feb 25 '22 at 13:35
5

Expanding on Kerrek's answer: I suggest using unique_ptr with custom deleter to handle the destruction for you automatically. You can wrap everything up nicely in a factory (Live at Rextester):

struct placement_deleter {
  template <typename T>
  void operator () (T* ptr) const {
    ptr->~T();
  }
};

template <typename T, typename...Args>
std::unique_ptr<T, placement_deleter>
make_in_place(void* place, Args&&...args) {
  return std::unique_ptr<T, placement_deleter>{
    ::new (place) T(std::forward<Args>(args)...)
  };
}

int main() {
  std::aligned_union<0, int, std::string>::type storage;
  {
    auto i = make_in_place<int>(&storage, 42);
    std::cout << *i << '\n';
  }
  {
    auto s = make_in_place<std::string>(&storage, "this is");
    *s += " a test";
    std::cout << *s << '\n';
  }
}
Community
  • 1
  • 1
Casey
  • 41,449
  • 7
  • 95
  • 125
2

I'm also expanding on Kerrek's answer. A possible (C++14) implementation of aligned_union is:

template <std::size_t Len, class... Types>
struct aligned_union
{
  static constexpr std::size_t alignment_value = std::max({alignof(Types)...});

  struct type
  {
    alignas(alignment_value) char _s[std::max({Len, sizeof(Types)...})];
  };
};

So it's clear that:

  • you have to construct your own object (placement new)
  • type is a POD type of a size and alignment suitable for use as uninitialized storage for an object of any of the types listed in Types (but actually none of those types)
  • you can use 0 as value for Len parameter (I think it's for generic programming situations, e.g. see https://stackoverflow.com/a/27069379/3235496)

For further details:

Community
  • 1
  • 1
manlio
  • 18,345
  • 14
  • 76
  • 126