Когда компилятору разрешено оптимизировать инициализацию стиля авто + скобки?

Предположим, у вас есть класс под названием Product, определенный следующим образом:

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;
   };

и вы инициализируете такую ​​переменную:

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

Я думал, что стандарт диктует, что компилятор должен логически делать следующее:

  • создать временный продукт
  • move-construct p (с использованием временного продукта)

Но компилятору было разрешено оптимизировать это так, чтобы p строился напрямую.

Я проверил это (Visual Studio 2013), и действительно, компилятор оптимизирует это, даже если у нас есть собственный (не по умолчанию) конструктор перемещения. Это хорошо.

Однако, если я явно удалю конструктор копирования и перемещения, например:

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;
   };

Инициализация auto + brace все еще компилируется. Я думал, что компилятор должен был предотвратить это, потому что не разрешено копирование или перемещение.

Как ни странно, если я сделаю удаленный конструктор копирования и перемещения закрытым, например:

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;
   };

Тогда инициализация auto + brace больше не компилируется.

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

Это ожидаемое поведение? Это ошибка в Visual Studio 2013 (обновление 3)?

Примечание: я попытался скомпилировать это на ideone, и там он действительно отказывается компилировать инициализацию, когда конструкторы копирования и перемещения удаляются (и становятся общедоступными). Так что я думаю, что это ошибка Visual Studio.


person Patrick    schedule 26.06.2015    source источник
comment
К вашему сведению, в Visual Studio 2013 сгенерированные компилятором конструкторы перемещения и операторы присваивания перемещения не поддерживаются (хотя вы можете определить свои собственные). Вот список (несоответствие) VS2013 C ++ 11   -  person Cory Kramer    schedule 26.06.2015
comment
Версия компилятора 18.00.30723. Может ли эта ошибка появиться между 21005 и 30723 годами?   -  person Patrick    schedule 26.06.2015
comment
@ Патрик NVM. Как только я предоставил пустые скобки для конструктора и деструктора, он скомпилирован на VS.   -  person NathanOliver    schedule 26.06.2015
comment
@CoryKramer. Дело не в конструкторе перемещения по умолчанию, созданном компилятором. Речь идет о том, что когда я явно удаляю конструктор перемещения, код все равно компилируется. И я думаю, что это не допускается стандартом.   -  person Patrick    schedule 26.06.2015
comment
@Patrick Согласен, проблема не в этом. Я просто подумал, пока вы играли в эти воды, я предупреждал вас о некоторых известных проблемах, связанных со ссылками на r-значение, перемещением и т. Д.   -  person Cory Kramer    schedule 26.06.2015
comment
Только что проверил Visual Studio 2015: здесь выдается ошибка (что правильно): ошибка C2280: 'Product :: Product (Product &&)': попытка ссылки на удаленную функцию   -  person Patrick    schedule 22.07.2015
comment
@CoryKramer: Visual Studio 2015 теперь правильно обрабатывает конструкторы перемещения по умолчанию. Ура.   -  person Patrick    schedule 22.07.2015


Ответы (2)


стандарт очень ясен, как вы упомянули ранее, указывая на то, что это ошибка в компиляторе cl. Вы никогда не можете быть уверены, хотя, если один компилятор что-то говорит, а все остальные не согласны, я ожидаю, что это будет одна из многих нестандартных реализаций компилятора MSVC.

Интерпретация clang версии 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

Интерпретация 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

Также помните, что явно заданные по умолчанию и удаленные функции появились в MSVC. 2013 г., а его реализация еще не завершена. Вроде еще не понимает = default для конструкторов перемещения.

Я предполагаю, что MSVC 2013 не проверяет конструктор перемещения или просто возвращается к конструктору копирования.

Было бы интересно проверить MSVC 2015, поскольку он, похоже, имеет (более) полную реализацию этих конструкций.

JVApen

person JVApen    schedule 28.06.2015
comment
Visual Studio 2015 действительно выдает ошибку (Product :: Product (Product &&) ': пытается сослаться на удаленную функцию). Так что это явно была ошибка в Visual Studio 2013. - person Patrick; 22.07.2015

В твоей строке

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

знак равенства не обозначает оператор присваивания, а просто синтаксис инициализатора. Следовательно, компилятор ничего не оптимизирует, а просто выполняет инициализацию.

person nv3    schedule 26.06.2015
comment
Тогда почему он терпит неудачу, когда конструктор перемещения и копирования удаляется И закрывается? - person Patrick; 27.06.2015
comment
Это не так. Согласно стандарту компилятор должен сначала создать временный объект, а затем переместить его в переменную. Но компилятору разрешено оптимизировать это (построение переменной напрямую) при условии, что логический эффект будет таким же, как и конструкция + moveconstruction, чего не происходит при удалении moveconstructor. - person Patrick; 28.06.2015