Bug 36461 - [c++0x] Exception throws don't use rvalue reference constructors
Summary: [c++0x] Exception throws don't use rvalue reference constructors
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 4.3.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2008-06-07 20:18 UTC by Niall Douglas
Modified: 2008-09-24 20:20 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments
Working (305 bytes, text/plain)
2008-06-08 17:07 UTC, Niall Douglas
Details
Failing (308 bytes, text/plain)
2008-06-08 17:19 UTC, Niall Douglas
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Niall Douglas 2008-06-07 20:18:59 UTC
I will firstly admit that I don't know if exception throws /should/ use rvalue reference constructors - the proposed working at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2118.html doesn't mention it, but one certainly would have thought it would.

In my project I have a destructively copied exception class. Currently, the standard copy constructor simply moves a pointer to an internal state object which contains a large amount of debug data eg; stack backtraces. Without this the debug build can be very slow and stack usage dangerous (there is a bug in MSVC where copy construction of an exception during a throw doesn't release previous copies which causes quick exhaustion of the stack).

I have tried to add an rvalue reference constructor under the idea that with -std=c++0x it would implement move semantics more legally than at current. Unfortunately, g++ really wants to use the copy constructor for a thrown exception and won't accept a rvalue copy constructor.

Is this intentional?
Comment 1 Niall Douglas 2008-06-08 17:07:26 UTC
Created attachment 15732 [details]
Working
Comment 2 Niall Douglas 2008-06-08 17:19:02 UTC
This problem actually seems to be one of subclassing: child class rvalue constructors invoke base class lvalue constructors!!!

I have attached an example. As is, it compiles and works. If however you throw a Thing2 instead of Thing, you get:

ned@kate:~/Tornado/Tn/TClient/TnFOX$ g++ -o TestCPP0x -std=c++0x TestCPP0x.cpp
TestCPP0x.cpp: In copy constructor ‘Thing2::Thing2(const Thing2&)’:
TestCPP0x.cpp:9: error: ‘Thing::Thing(const Thing&)’ is private
TestCPP0x.cpp:12: error: within this context
TestCPP0x.cpp: In function ‘Thing2 f(bool)’:
TestCPP0x.cpp:23: note: synthesized method ‘Thing2::Thing2(const Thing2&)’ first required here

This is despite that Thing2 defines no constructors at all apart from the default. Ok, so I tried manually disabling the lvalue constructor in Thing2 like this:

class Thing2 : public Thing {
public:
    Thing2() { }
    Thing2(Thing2&& o) : Thing(o) { }
private:
    Thing2(const Thing2&);
};

... and now I get:

ned@kate:~/Tornado/Tn/TClient/TnFOX$ g++ -o TestCPP0x -std=c++0x TestCPP0x.cpp
TestCPP0x.cpp: In constructor ‘Thing2::Thing2(Thing2&&)’:
TestCPP0x.cpp:9: error: ‘Thing::Thing(const Thing&)’ is private
TestCPP0x.cpp:15: error: within this context

In other words, the rvalue constructor in Thing2 is trying to invoke the lvalue constructor in Thing!!! This is surely utterly wrong unless Thing has no rvalue constructor. Our Thing class has the lvalue disabled and rvalue enabled, so GCC is surely making a big mistake.

This is the shortest example of what went wrong with my exception throwing problem.

There's an additional problem. If I don't specify any constructors at all for Thing2 apart the default constructor, GCC /should/ generate synthesised ones based on what's available in the parent classes. Unfortunately, GCC is ignoring that the lvalue constructor has been disabled and tries to generate a lvalue constructor anyway which is obviously doomed to failure.

GCC 3.4.1 just went into the Ubuntu repositories, so I'll try that next.

Niall
Comment 3 Niall Douglas 2008-06-08 17:19:42 UTC
Created attachment 15733 [details]
Failing
Comment 4 dgregor 2008-09-24 20:20:21 UTC
GCC is doing the right thing here. In this constructor:

  Thing2(Thing2&& o) : Thing(o) { }

the parameter "o" is treated as an lvalue, because it has a name. Using std::move(o) to treat it as an rvalue.

Similarly, there are no automatically generated move constructors or move-assignment operators, so if you leave Thing2() empty, you'll just get the copy constructor and therefore do a copy.