среда, 23 июля 2014 г.

[prog.c++] Небольшая ошибка у Страуструпа в C++11 FAQ

Продолжая штудировать C++11 FAQ Бьёрна Страуструпа обнаружил маленькую ошибку в двух примерах кода:

Это в разделе "Algorithms improvements".

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

Именно потому, что в этом в принципе нет смысла, в std::unique_ptr конструктор и оператор копирования, получающие в качестве аргумента константную ссылку на std::unique_ptr, помечены как delete, т.е. запрещены к использованию. Именно поэтому, если у нас есть:

void f(std::unique_ptr<Big> a) {...}

Мы не можем написать так:

std::unique_ptr<Big> b(...);
f(b);

А должны писать одним из следующих способов:

f(std::move(b)); // Явное преобразование к rvalue reference.
                 // После этой конструкции b уже ничего не контролирует,
                 // владение объектом Big передано аргументу функции f().

f(std::unique_ptr<Big>(new Big(...))); // Неявный rvalue reference.
                 // Для конструирования аргумента a функции f()
                 // вызывается move constructor для std::unique_ptr.

Поэтому код из примеров Страуструпа не скомпилируется. Для того, чтобы функторы работали так, как задумывалось, из прототипы должны были бы быть объявлены вот так:

template<class P> struct Cmp { // compare *P values
  bool operator() (const P & a, const P & b) const { return *a<*b; }
 }

и

sort(vb.begin(),vb.end(),
  [](const unique_ptr<Big> & a, const unique_ptr<Big> & b) { return *a<*b; });

Под катом автономный пример, с которым можно поиграться самостоятельно.

#include <vector>
#include <memory>
#include <algorithm>
#include <iostream>
#include <string>

class My
   {
   public :
      My( std::string s )
         :  s_( std::move( s ) )
         {
            std::cout << "M('" << s_ << "')" << std::endl;
         }
      ~My()
         {
            std::cout << "~M('" << s_ << "')" << std::endl;
         }

      const std::string &
      get() const
         {
            return s_;
         }

   private :
      const std::string s_;
   };

templateclass P > struct Cmp
   {
      bool operator()( const P & a, const P & b ) const {
         return a->get() < b->get();
      }
   };

int main()
   {
      std::vector< std::unique_ptr< My > > v;
      v.emplace_back( std::unique_ptr< My >( new My( "One" ) ) );
      v.emplace_back( std::unique_ptr< My >( new My( "Two" ) ) );
      v.emplace_back( std::unique_ptr< My >( new My( "Three" ) ) );
      v.emplace_back( std::unique_ptr< My >( new My( "Four" ) ) );

      std::cout << "sorting..." << std::endl;

      std::sort( v.begin(), v.end(),
         []( const std::unique_ptr< My > & a, const std::unique_ptr< My > & b )
         {
            return a->get() < b->get();
         } );

      std::sort( v.begin(), v.end(), Cmp<std::unique_ptr<My> >() );

      std::cout << "sorting result: " << std::endl;
      forauto & s : v )
         std::cout << s->get() << std::endl;
   }

Комментариев нет: