| In modern gcc, std::variant doesn't look like a competent replacement for old-fashioned tagged unions. On x86_64 Linux, it looks like a function with signature `void f(std::variant<int, char>)` expects its (8-byte) argument to be passed by reference, whereas `void f2(tagged_union_of_int_and_char)` passes its argument in rdi. gcc (-O3) also generates miserable code for calling f(42): 4004f0: 48 83 ec 18 sub $0x18,%rsp
4004f4: 48 89 e7 mov %rsp,%rdi
4004f7: c7 04 24 2a 00 00 00 movl $0x2a,(%rsp)
4004fe: c6 44 24 04 00 movb $0x0,0x4(%rsp)
400503: e8 c8 ff ff ff callq 4004d0 <f>
400508: 48 83 c4 18 add $0x18,%rsp
40050c: c3 retq
as compared with calling f2(42): 400510: 48 bf 00 00 00 00 2a movabs $0x2a00000000,%rdi
400517: 00 00 00
40051a: eb c4 jmp 4004e0 <f2>
std::optional seems to have the same disease; the ABI is different from a plain struct containing a bool and the value and you get worse initialisation code. There's also a build time penalty to using std::optional; on my machine, including the code that uses optionals makes my trivial test program take 320ms to compile and link rather than 50ms.These look like yet more new C++ features that are worse than what they're trying to replace. |
Now why it is not trivially copy constructible is another question. But the latest standard draft does not require it [variant.ctor] though it does require the destructor to be trivial if all type destructors are trivial [variant.dtor]. The GCC implementation also seems to ensure only trivial destructor not copy constructor (the code is here [1] if anyone likes to see).
EDIT: I did a small experiment that agrees with this - adding a copy constructor causes passing by pointer. https://godbolt.org/g/UG0YXJ
EDIT2: I filed a bug report for gcc, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80187
[1] https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-...