Почему С++ destuctor влияет на поведение оптимизации возвращаемого значения

Я упростил свой код следующим образом.

#include <vector>
class NoncopyableItem {
 public:
  NoncopyableItem() { }
  NoncopyableItem(NoncopyableItem &&nt) { };
};
class Iterator {
  friend class Factory;
 public:
  ~Iterator() { }  // weird
 private:
  Iterator() { }
  std::vector<NoncopyableItem> buffer_;
};
class Factory {
 public:
  Iterator NewIterator() {
    return Iterator();
  }
};
int main() {
  Factory fa;
  auto it = fa.NewIterator();
  return 0;
}

Я хочу использовать RVO (оптимизацию возвращаемого значения) в функции NewIterator, но я получил следующую ошибку:

In file included from /usr/lib/gcc/x86_64-pc-cygwin/4.9.3/include/c++/vector:62:0,
                 from /cygdrive/c/Users/DELL/ClionProjects/destructor_test/main.cpp:1:
/usr/lib/gcc/x86_64-pc-cygwin/4.9.3/include/c++/bits/stl_construct.h: In instantiation of 'void std::_Construct(_T1*, _Args&& ...) [with _T1 = NoncopyableItem; _Args = {const NoncopyableItem&}]':
/usr/lib/gcc/x86_64-pc-cygwin/4.9.3/include/c++/bits/stl_uninitialized.h:75:53:   required from 'static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const NoncopyableItem*, std::vector<NoncopyableItem> >; _ForwardIterator = NoncopyableItem*; bool _TrivialValueTypes = false]'
/usr/lib/gcc/x86_64-pc-cygwin/4.9.3/include/c++/bits/stl_uninitialized.h:126:41:   required from '_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const NoncopyableItem*, std::vector<NoncopyableItem> >; _ForwardIterator = NoncopyableItem*]'
/usr/lib/gcc/x86_64-pc-cygwin/4.9.3/include/c++/bits/stl_uninitialized.h:279:63:   required from '_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = __gnu_cxx::__normal_iterator<const NoncopyableItem*, std::vector<NoncopyableItem> >; _ForwardIterator = NoncopyableItem*; _Tp = NoncopyableItem]'
/usr/lib/gcc/x86_64-pc-cygwin/4.9.3/include/c++/bits/stl_vector.h:324:32:   required from 'std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = NoncopyableItem; _Alloc = std::allocator<NoncopyableItem>]'
/cygdrive/c/Users/DELL/ClionProjects/destructor_test/main.cpp:7:7:   required from here
/usr/lib/gcc/x86_64-pc-cygwin/4.9.3/include/c++/bits/stl_construct.h:75:7: error: use of deleted function 'constexpr NoncopyableItem::NoncopyableItem(const NoncopyableItem&)'
     { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
       ^
/cygdrive/c/Users/DELL/ClionProjects/destructor_test/main.cpp:2:7: note: 'constexpr NoncopyableItem::NoncopyableItem(const NoncopyableItem&)' is implicitly declared as deleted because 'NoncopyableItem' declares a move constructor or move assignment operator
 class NoncopyableItem {
       ^
CMakeFiles/destructor_test.dir/build.make:62: recipe for target 'CMakeFiles/destructor_test.dir/main.cpp.o' failed

Согласно cppreference.com, NewIterator() должно соответствовать требованию RVO. Однако кажется, что компилятор пытается вызвать конструктор копии по умолчанию Iterator, а затем не работает, потому что Iterator.buffer_ не подлежит копированию.

Хорошо, к моему удивлению, если я удалю деструктор Iterator в L # 13, код будет работать нормально.

Почему деструктор влияет на поведение RVO компилятора?

Ответ 1

Прежде всего, забудьте о RVO в этом контексте. Это юридическая оптимизация, но даже когда это происходит, код должен быть законным без него.

Таким образом, имея в виду, мы смотрим на

auto it = fa.NewIterator();

Эта строка пытается построить новый Iterator из временного Iterator. Для этого нам нужно либо одно из следующих двух :

Iterator(const Iterator&); //or
Iterator(Iterator&&);

Теперь в опубликованном вами коде, пытаясь использовать неявно объявленный Iterator(const Iterator&);, вы получите ошибку компилятора, которую вы показали, потому что конструктор копирования нестатического элемента buffer_ не удается скомпилировать.

Второй кандидат не создается, поскольку Iterator имеет определяемый пользователем деструктор.

Если вы удалите определяемый пользователем деструктор, компилятор сгенерирует конструктор перемещения Iterator(Iterator&&); и будет использовать его при создании из временного. Или, может быть, это не будет и RVO вместо этого, но он может использовать его, и это важная часть.


Или какой-либо другой объявленный конструктор, который делает линию законной, конечно. Но выше приведены два обычных компилятора, которые вы, по-видимому, задаете.