When is the compiler allowed to optimize auto+brace style initialization?
Asked Answered
P

2

6

Suppose you have a class called Product, defined like this:

class Product
   {
   public:
      Product(const char *name, int i);
      Product(Product &&rhs);
      Product(const Product &rhs);
      ~Product();
   private:
      const char *m_name;
      int m_i;
   };

and you initialize a variable like this:

auto p = Product{"abc",123};

I thought that the standard dictated that a compiler must logically do the following:

  • construct a temporary Product
  • move-construct p (using the temporary Product)

But that the compiler was allowed to optimize this so that p is directly constructed.

I verified this (Visual Studio 2013) and indeed, the compiler optimizes this, even if we have our own custom (non-default) move-constructor. This is fine.

However, if I explicitly delete the copy- and move-constructor, like this:

class Product
   {
   public:
      Product(const char *name, int i);
      Product(Product &&rhs) = delete;
      Product(const Product &rhs) = delete;
      ~Product();
   private:
      const char *m_name;
      int m_i;
   };

The auto+brace initialization still compiles. I though the compiler had to prevent this because there is no copy- or move- allowed.

Strange enough, if I make the deleted copy- and move-constructor private, like this:

class Product
   {
   public:
      Product(const char *name, int i);
      ~Product();
   private:
      Product(Product &&rhs) = delete;
      Product(const Product &rhs) = delete;
      const char *m_name;
      int m_i;
   };

Then the auto+brace initialization doesn't compiler anymore.

error C2248: 'Product::Product' : cannot access private member declared in class 'Product'

Is this expected behavior? Is this a bug in Visual Studio 2013 (Update 3)?

Note: I tried compiling this on ideone and there it indeed refuses to compile the initialization when the copy- and move-constructors are deleted (and public). So I think this is a Visual Studio bug.

Polyhistor answered 26/6, 2015 at 16:13 Comment(7)
Just FYI in Visual Studio 2013, compiler generated move constructors and move assignment operators are not supported (though you may define your own). Here is a list of VS2013's C++11 (non)complianceUnearth
Compiler version 18.00.30723. Could this bug be introduced between 21005 and 30723?Polyhistor
@Polyhistor NVM. Once I provided empty brackets for the constructor and the destructor it compiled on VS.Confiture
@CoryKramer. It's not about the default-compiler-generated move constructor. It's about when I explicitly delete the move-constructor, the code still compiles. And I think this is not allowed by the standard.Polyhistor
@Polyhistor Agreed, that is not the source of your issue. I just figured while you were playing around those waters I'd warn you about some known issues related to r-value references, moving, etc.Unearth
Just checked Visual Studio 2015: here an error is given (which is correct): error C2280: 'Product::Product(Product &&)': attempting to reference a deleted functionPolyhistor
@CoryKramer: Visual Studio 2015 now correctly handles the =default move constructors. Hooray.Polyhistor
H
1

the standard is very clear as you mentioned before, indicating that this is a bug in the cl-compiler. You never can be sure, though if one compiler is saying something and all others disagree, I expect that this would be one of the many non-standard-compliant implementations of the MSVC compiler.

The interpretation of clang version 3.7 (svn-build):

t.cpp:19:7:{19:11-19:30}: error: call to deleted constructor of 'Product'
      [Semantic Issue]
        auto p = Product{"abc", 123};
              ^   ~~~~~~~~~~~~~~~~~~~
t.cpp:8:2: note: 'Product' has been explicitly marked deleted here
      [Semantic Issue]
        Product(Product &&rhs) = delete;
         ^
1 error generated.
make: *** [t.o] Error 1

The interpretation of gcc 4.8:

t.cpp: In function ‘int main()’:
t.cpp:19:29: error: use of deleted function ‘Product::Product(Product&&)’
  auto p = Product{"abc", 123};
                             ^
t.cpp:8:2: error: declared here
  Product(Product &&rhs) = delete;
  ^
make: *** [build/gcc/t.o] Error 1

Keep also in mind that the Explicitly Defaulted and Deleted Functions are new since MSVC 2013 and the implementation of it is not yet complete. Like it does not yet understand =default for move constructors.

My guess would be that MSVC 2013 does not check for the move constructor or simply falls back to the copy constructor.

It might be interesting to check MSVC 2015, since it appears to have a (more) complete implementation of these constructions.

JVApen

Hammurabi answered 28/6, 2015 at 14:11 Comment(1)
Visual Studio 2015 is indeed giving an error on this (Product::Product(Product &&)': attempting to reference a deleted function). So it was clearly a bug in Visual Studio 2013.Polyhistor
S
0

In your line

auto p = Product{"abc",123};

the equal sign is not the denoting the assignment operator, but is just the syntax for an initializer. Hence, the compiler is not optimizing anything, but just doing the initialization.

Spiegelman answered 26/6, 2015 at 16:39 Comment(2)
Then why does it fail when the move- and copy-constructor are deleted AND private?Polyhistor
This is not correct. According to the standard the compiler must first construct a temporary, then move-construct it into the variable. But the compiler is allowed to optimize this (constructing the variable directly), provided the logical effect would be the same as construction+moveconstruction, which is not the case if the moveconstructor is deleted.Polyhistor

© 2022 - 2024 — McMap. All rights reserved.