I was wondering what could be causing segfaults in this simple example using std::experimental::optional and a union type. What's curious is that a segfault happens in both clang and gcc, but in two different places.
I'm also baffled at the obscene amount of copying and destructing going on which is seen from the logs below, wonder if there's a better/idiomatic way to avoid so many obviously redundant operations? In cases like this, given that all objects here are passed and accessed by-value, would it help to switch all constructors to take rvalue references and use std::move everywhere?
#include <iostream>
#include <vector>
// https://github.com/akrzemi1/Optional
#include "Optional/optional.hpp"
using std::cout;
using std::vector;
using std::experimental::optional;
struct X {
int y;
X(int y) : y(y) { cout << "X::X(int)\n"; }
X(const X& x) : y(x.y) { cout << "X::X(const X&)\n"; }
~X() noexcept { cout << "X::~X()\n"; }
};
struct A {
vector<X> x;
A(const vector<X>& x) : x(x) { cout << "A::A(const vector<X>&)\n"; }
A(const A& a) : x(a.x) { cout << "A::A(const A&)\n"; }
~A() noexcept { cout << "A::~A()\n"; }
static optional<A> get() {
cout << "A::get()\n";
return A({ X(1), X(2) });
}
};
struct M {
union { A a; };
M(A a) : a(a) {cout << "M::M(A)\n";}
M(const M &m) { a = m.a; }
~M() noexcept {
cout << "M::~M()\n";
(&a)->A::~A();
}
static optional<M> get() {
cout << "M::get()\n";
auto a = A::get();
return M(*a);
}
};
struct P {
vector<M> m;
P(const vector<M>& m) : m(m) { cout << "P::P(const vector<M>&)\n"; }
P(const P& p) : m(p.m) { cout << "P::P(const P&)\n"; }
static optional<P> get() {
cout << "P::get()\n";
auto m1 = M::get();
auto m2 = M::get();
vector<M> m;
cout << "push message 1\n";
m.push_back(*m1);
cout << "push message 2\n";
m.push_back(*m2);
return P(m);
}
};
int main() {
auto p = P::get();
cout << (*p).m[1].a.x[0].y << "\n";
}
GCC fails like this:
P::get()
M::get()
A::get()
X::X(int)
X::X(int)
X::X(const X&)
X::X(const X&)
X::X(const X&)
X::X(const X&)
A::A(const vector<X>&)
X::X(const X&)
X::X(const X&)
A::A(const A&)
A::~A()
X::~X()
X::~X()
X::~X()
X::~X()
X::~X()
X::~X()
X::X(const X&)
X::X(const X&)
A::A(const A&)
M::M(A)
X::X(const X&)
X::X(const X&)
A::A(const A&)
'./a.out' terminated by signal SIGBUS (Misaligned address error)
#0 0x0000000100003c59 in X* std::__copy_move<false, false, std::random_access_iterator_tag>::__copy_m<X const*, X*>(X const*, X const*, X*) ()
#1 0x000000010000364e in X* std::__copy_move_a<false, X const*, X*>(X const*, X const*, X*) ()
#2 0x0000000100002f3c in __gnu_cxx::__normal_iterator<X*, std::vector<X, std::allocator<X> > > std::__copy_move_a2<false, __gnu_cxx::__normal_iterator<X const*, std::vector<X, std::allocator<X> > >, __gnu_cxx::__normal_iterator<X*, std::vector<X, std::allocator<X> > > >(__gnu_cxx::__normal_iterator<X const*, std::vector<X, std::allocator<X> > >, __gnu_cxx::__normal_iterator<X const*, std::vector<X, std::allocator<X> > >, __gnu_cxx::__normal_iterator<X*, std::vector<X, std::allocator<X> > >) ()
#3 0x00000001000025f8 in __gnu_cxx::__normal_iterator<X*, std::vector<X, std::allocator<X> > > std::copy<__gnu_cxx::__normal_iterator<X const*, std::vector<X, std::allocator<X> > >, __gnu_cxx::__normal_iterator<X*, std::vector<X, std::allocator<X> > > >(__gnu_cxx::__normal_iterator<X const*, std::vector<X, std::allocator<X> > >, __gnu_cxx::__normal_iterator<X const*, std::vector<X, std::allocator<X> > >, __gnu_cxx::__normal_iterator<X*, std::vector<X, std::allocator<X> > >) ()
#4 0x0000000100001d19 in std::vector<X, std::allocator<X> >::operator=(std::vector<X, std::allocator<X> > const&) ()
#5 0x00000001000012ad in A::operator=(A const&) ()
#6 0x00000001000012d7 in M::M(M const&) ()
#7 0x0000000100001356 in std::experimental::storage_t<M>::storage_t<M>(M&&) ()
#8 0x0000000100001393 in std::experimental::optional_base<M>::optional_base(M&&) ()
#9 0x00000001000013c4 in std::experimental::optional<M>::optional(M&&) ()
#10 0x0000000100001456 in M::get() ()
#11 0x00000001000016a8 in P::get() ()
#12 0x0000000100000db1 in main ()
whereas clang sometimes doesn't crash and sometimes does like so:
P::get()
M::get()
A::get()
X::X(int)
X::X(int)
X::X(const X&)
X::X(const X&)
X::X(const X&)
X::X(const X&)
A::A(const vector<X>&)
X::X(const X&)
X::X(const X&)
A::A(const A&)
A::~A()
X::~X()
X::~X()
X::~X()
X::~X()
X::~X()
X::~X()
X::X(const X&)
X::X(const X&)
A::A(const A&)
M::M(A)
X::X(const X&)
X::X(const X&)
A::A(const A&)
X::X(const X&)
X::X(const X&)
M::~M()
A::~A()
X::~X()
X::~X()
A::~A()
X::~X()
X::~X()
A::~A()
X::~X()
X::~X()
M::get()
A::get()
X::X(int)
X::X(int)
X::X(const X&)
X::X(const X&)
X::X(const X&)
X::X(const X&)
A::A(const vector<X>&)
X::X(const X&)
X::X(const X&)
A::A(const A&)
A::~A()
X::~X()
X::~X()
X::~X()
X::~X()
X::~X()
X::~X()
X::X(const X&)
X::X(const X&)
A::A(const A&)
M::M(A)
X::X(const X&)
X::X(const X&)
A::A(const A&)
X::X(const X&)
X::X(const X&)
M::~M()
A::~A()
X::~X()
X::~X()
A::~A()
X::~X()
X::~X()
A::~A()
X::~X()
X::~X()
push message 1
'./a.out' terminated by signal SIGSEGV (Address boundary error)
You are not constructing the A
in the M const&
ctor case, you are just assigning to it when it is uninitialized.
Unions do not construct their contents.
This has nothing to do with optional.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With