Created attachment 27291 [details] Simple test program When compiling and running the attached program the copy constructor for background_hello should be called at least once. It is not being called at all, but the function call operator is showing a different this pointer, and the destructor is called 3 times. ~$ g++ --version g++ (Ubuntu/Linaro 4.7.0-7ubuntu1) 4.7.0 Copyright (C) 2012 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ~$ g++ -std=c++0x -pthread t.cpp && ./a.out default ctor called, this=0x7fffc2b05bdf destructor called, this=0x7fffc2b05b8f void background_hello::operator()() called, this=0x922040 destructor called, this=0x922040 destructor called, this=0x7fffc2b05bdf
Ouch. With 4.6 I see three ctors and three dtors: default ctor called, this=0x7fffafe073df copy ctor called move ctor called destructor called, this=0x7fffafe073a0 void background_hello::operator()() called, this=0x15f0048 destructor called, this=0x15f0048 destructor called, this=0x7fffafe073df
Here's a reduced version that only needs -std=c++11 and not -pthread #include <tuple> template<typename Callable> struct Bind_simple { explicit Bind_simple(const Callable& callable) : _M_bound(callable) { } Bind_simple(const Bind_simple&) = default; Bind_simple(Bind_simple&&) = default; auto operator()() -> decltype((*(Callable*)0)()) { return std::get<0>(_M_bound)(); } private: std::tuple<Callable> _M_bound; }; template<typename Callable> Bind_simple<Callable> bind_simple(Callable& callable) { return Bind_simple<Callable>(callable); } struct thread { struct ImplBase { }; template<typename T> struct Impl : ImplBase { T t; Impl(T&& t) : t(std::move(t)) { } }; template<typename T> thread(T& t) { auto p = make_routine(bind_simple(t)); p->t(); delete p; } template<typename Callable> Impl<Callable>* make_routine(Callable&& f) { return new Impl<Callable>(std::forward<Callable>(f)); } }; class background_hello { public: background_hello() { __builtin_printf("default ctor called, this=%p\n", this); } background_hello(const background_hello &) { __builtin_printf("copy ctor called\n"); } background_hello(background_hello &&) { __builtin_printf("move ctor called\n"); } void operator ()() const { __builtin_printf("void background_hello::operator()() called, this=%p\n", this); } ~background_hello() { __builtin_printf("destructor called, this=%p\n", this); } }; int main() { background_hello bh; thread t(bh); } This still produces the wrong result: default ctor called, this=0x7fffe4a2c3bf destructor called, this=0x7fffe4a2c38f void background_hello::operator()() called, this=0xd9d028 destructor called, this=0xd9d028 destructor called, this=0x7fffe4a2c3bf Looking at the gimple dump I see that the thread::Impl<Bind_simple<background_hello()>> constructor doesn't initialize its Bind_simple member: thread::Impl<T>::Impl(T&&) [with T = Bind_simple<background_hello>] (struct Impl * const this, struct Bind_simple & t) { struct Bind_simple * D.8909; thread::ImplBase::ImplBase (this); try { } catch { D.8909 = &this->t; Bind_simple<background_hello>::~Bind_simple (D.8909); } } If the Bind_simple object isn't move-constructed then its background_hello object won't be either, which explains the missing constructor calls. If Bind_simple<Callable> stores a Callable directly instead of as tuple<Callable> then the problem goes away and I see three constructors called. I think at this point it looks like a G++ issue
If I change some of the functions to forward lvalues instead of using perfect forwarding then I see one call to a copy constructor e.g. just changing the thread::Impl ctor to Impl(const T& t) : t(t) { } produces: default ctor called, this=0x7fffd878f35f copy ctor called destructor called, this=0x7fffd878f327 void background_hello::operator()() called, this=0x1258010 destructor called, this=0x1258010 destructor called, this=0x7fffd878f35f The gimple dump shows: thread::Impl<T>::Impl(const T&) [with T = Bind_simple<background_hello>] (struct Impl * const this, const struct Bind_simple & t) { struct Bind_simple * D.8913; struct Bind_simple * D.8914; thread::ImplBase::ImplBase (this); D.8913 = &this->t; Bind_simple<background_hello>::Bind_simple (D.8913, t); try { } catch { D.8914 = &this->t; Bind_simple<background_hello>::~Bind_simple (D.8914); } } Note the copy construction of the Bind_simple member that was missing previously
Created attachment 27301 [details] preprocessed source This preprocessed source was created by G++ version 4.6.3 20120306 (Red Hat 4.6.3-2) When compiled with 4.6.3 it gives: default ctor called, this=0x7fffba05109f copy ctor called, this=0x7fffba051067 move ctor called, this=0x253d010 destructor called, this=0x7fffba051067 void background_hello::operator()() called, this=0x253d010 destructor called, this=0x253d010 destructor called, this=0x7fffba05109f When the same preprocessed source is compiled with the 4.7 branch or trunk it gives: default ctor called, this=0x7fff8a2f7daf move ctor called, this=0x1d56010 destructor called, this=0x7fff8a2f7d77 void background_hello::operator()() called, this=0x1d56010 destructor called, this=0x1d56010 destructor called, this=0x7fff8a2f7daf N.B. there is only one missing constructor here, compared to two when the original source is compiled with trunk, I guess that's due to a change in <tuple> between 4.6 and 4.8, but the same preprocessed input should still produce the same result with both versions, so I think that rules out library changes being the sole cause of this bug
For that preprocessed source the difference is in the Bind_simple constructor, for 4.6 it is: Bind_simple<Callable>::Bind_simple(const Callable&) [with Callable = background_hello] (struct Bind_simple * const this, const struct background_hello & callable) { struct tuple * D.5382; struct tuple * D.5383; D.5382 = &this->_M_bound; std::tuple<background_hello>::tuple (D.5382, callable); try { } catch { D.5383 = &this->_M_bound; std::tuple<background_hello>::~tuple (D.5383); } } But for 4.8 it fails to initialise its member _M_bound: Bind_simple<Callable>::Bind_simple(const Callable&) [with Callable = background_hello] (struct Bind_simple * const this, const struct background_hello & callable) { struct tuple * D.6070; try { } catch { D.6070 = &this->_M_bound; std::tuple<background_hello>::~tuple (D.6070); } }
Based on comment 4 I'm marking this as a regression since 4.6
It is caused by revision 178518: http://gcc.gnu.org/ml/gcc-cvs/2011-09/msg00129.html
Author: jason Date: Mon Jun 25 15:17:59 2012 New Revision: 188940 URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=188940 Log: PR c++/53202 * semantics.c (build_data_member_initialization): Always keep initializer for empty base. (cxx_eval_bare_aggregate): Discard it here. Added: trunk/gcc/testsuite/g++.dg/cpp0x/constexpr-tuple.C Modified: trunk/gcc/cp/ChangeLog trunk/gcc/cp/semantics.c trunk/gcc/testsuite/ChangeLog
Author: jason Date: Mon Jun 25 15:41:13 2012 New Revision: 188941 URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=188941 Log: PR c++/53202 * semantics.c (build_data_member_initialization): Always keep initializer for empty base. (cxx_eval_bare_aggregate): Discard it here. Added: branches/gcc-4_7-branch/gcc/testsuite/g++.dg/cpp0x/constexpr-tuple.C Modified: branches/gcc-4_7-branch/gcc/cp/ChangeLog branches/gcc-4_7-branch/gcc/cp/semantics.c branches/gcc-4_7-branch/gcc/testsuite/ChangeLog
Fixed for 4.7.2