I'm seeing some different behavior between g++ and msvc around value initializing non-copyable objects. Consider a class that is non-copyable:
class noncopyable_base
{
public:
noncopyable_base() {}
private:
noncopyable_base(const noncopyable_base &);
noncopyable_base &operator=(const noncopyable_base &);
};
class noncopyable : private noncopyable_base
{
public:
noncopyable() : x_(0) {}
noncopyable(int x) : x_(x) {}
private:
int x_;
};
and a template that uses value initialization so that the value will get a known value even when the type is POD:
template <class T>
void doit()
{
T t = T();
...
}
and trying to use those together:
doit<noncopyable>();
This works fine on msvc as of VC++ 9.0 but fails on every version of g++ I tested this with (including version 4.5.0) because the copy constructor is private.
Two questions:
- Which behavior is standards compliant?
- Any suggestion of how to work around this in gcc (and to be clear, changing that to
T t;
is not an acceptable solution as this breaks POD types).
P.S. I see the same problem with boost::noncopyable.
There's §12.8/14:
And then there's §12.8/15:
So, the question is really, if the implementation omits the call to the copy constructor (which it is clearly allowed to do), is the copy constructor actually used?
And, the answer to that is yes, per §3.2/2:
Have you seen what happens when you compile using /Wall with MSVC? It states the following about your class:
GCC remedy: create a copy constructor for
noncopyable
(and an assignment operator ideally!) that does what it can to copy the information fromnoncopyable_base
, namely invoking the constructor fornoncopyable_base
that has no parameters (since that is the only one accessible bynoncopyable
) and then copying any data fromnoncopyable_base
. Given the definition ofnoncopyable_base
, however, it seems there is no data to copy, so the simple addition ofnoncopyable_base()
to the initializer list of a newnoncopyable(const noncopyable &)
function should work.Take note of what MSVC said about your program though. Also note that if you use
T t()
rather thanT t = T()
, another warning (C4930) is generated by MSVC, though GCC happily accepts it either way without any warning issued.The behavior you're seeing in MSVC is an extension, though it's documented as such in a roundabout way on the following page (emphasis mine) http://msdn.microsoft.com/en-us/library/0yw5843c.aspx:
See Ben Voigt's answer for a workaround which is a simplified version of
boost::value_initialized
, as pointed out by litb in a comment to Ben's answer. The docs forboost::value_initalized
has a great discussion of the problem, the workaround, and some of the pitfalls of various compiler issues.I don't think template metaprogamming is needed. Try