Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

non-copyable objects and value initialization: g++ vs msvc

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:

  1. Which behavior is standards compliant?
  2. 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.

like image 532
R Samuel Klatchko Avatar asked Dec 05 '22 03:12

R Samuel Klatchko


1 Answers

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:

The equal-sign initialization syntax is different from the function-style syntax, even though the generated code is identical in most cases. The difference is that when the equal-sign syntax is used, the compiler has to behave as if the following sequence of events were taking place:

  • Creating a temporary object of the same type as the object being initialized.
  • Copying the temporary object to the object.

The constructor must be accessible before the compiler can perform these steps. Even though the compiler can eliminate the temporary creation and copy steps in most cases, an inaccessible copy constructor causes equal-sign initialization to fail (under /Za, /Ze (Disable Language Extensions)).

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 for boost::value_initalized has a great discussion of the problem, the workaround, and some of the pitfalls of various compiler issues.

like image 81
Michael Burr Avatar answered Feb 12 '23 21:02

Michael Burr