Неужели реализация Hinnant unique_ptr неправильно преобразует производную в базу в этом случае? - PullRequest
3 голосов
/ 02 марта 2010

В настоящее время я пытаюсь использовать уникальную реализацию Говарда Хиннанта , и столкнулся с ошибкой компиляции. Вот пример кода:

struct Base {};

struct Derived : public Base {};

void testfun(boost::unique_ptr<Base>);

void test()
{
    unique_ptr<Derived> testDerived; 
    unique_ptr<Base> testBase(move(testDerived)); // ok, construct base explicitly from derived 
    testfun(move(testBase));                      // ok, pass base to testfun which expects base 
    testfun(unique_ptr<Base>(move(testDerived))); // ok, explicitly converts to unique_ptr<Base>
    testfun(move(testDerived));                   // error on this line
}

Я получаю ошибку

In function 'void test()':
error: no matching function for call to 'boost::unique_ptr<Base, boost::default_delete<Base> >::unique_ptr(boost::unique_ptr<Base, boost::default_delete<Base> >)'
note: candidates are: boost::unique_ptr<T, D>::unique_ptr(boost::detail_unique_ptr::rv<boost::unique_ptr<T, D> >) [with T = Base, D = boost::default_delete<Base>]
note:                 boost::unique_ptr<T, D>::unique_ptr(boost::unique_ptr<T, D>&) [with T = Base, D = boost::default_delete<Base>]
error:   initializing argument 1 of 'void testfun(boost::unique_ptr<Base, boost::default_delete<Base> >)' from result of 'boost::unique_ptr<T, D>::unique_ptr(boost::unique_ptr<U, E>, typename boost::enable_if_c<((((! boost::is_array<U>::value) && boost::detail_unique_ptr::is_convertible<typename boost::unique_ptr<U, boost::default_delete<U> >::pointer,typename boost::detail_unique_ptr::pointer_type<T, D>::type>::value) && boost::detail_unique_ptr::is_convertible<E,D>::value) && ((! boost::is_reference<D>::value) || boost::is_same<D,E>::value)), void>::type*) [with U = Derived, E = boost::default_delete<Derived>, T = Base, D = boost::default_delete<Base>]'

Кажется, что оскорбительная строка не должна потерпеть неудачу. Является ли это ошибкой в ​​реализации, ограничением реализации из-за отсутствия возможностей языка C ++ 0x или неправильного понимания правил unique_ptrs?

(Обратите внимание, я знаю, что это не будет работать во время выполнения, потому что я перемещаю одну и ту же вещь более одного раза; я просто пытаюсь выяснить ошибку времени компиляции.)

Ответы [ 2 ]

1 голос
/ 02 марта 2010

Для аналогичного примера смотрите этот пример, который тоже должен потерпеть неудачу

unique_ptr<Base> testBase = move(testDerived);

Проблема здесь в том, как реализована семантика перемещения: «конструктор копирования» использует неконстантную ссылку, поэтому не можетпривязать к временным.Чтобы все еще «уйти» от временных фигур, в классе есть функция преобразования (следующие на самом деле просто концептуальные - они могут быть по-разному реализованы в деталях):

operator rv<T>() { return rv<T>(*this); }

И конструктор примет этот объект:

unique_ptr(rv<T> r):ptr_(r.release()) { }

Вот пример, который не работает по той же причине:

// move helper. rv<> in unique_ptr
struct E { };

// simulates a unique_ptr<D>
struct D { };

// simulates the unique_ptr<B>
struct A {
  A() { }

  // accepts "derived" classes. Note that for unique_ptr, this will need that
  // the argument needs to be copied (we have a by-value parameter). Thus we 
  // automatically ensure only rvalue derived-class pointers are accepted.
  A(D) { } 

  // these will accept rvalues
  A(E) { }
  operator E() { return E(); }

private:
  A(A&); // private, error if passed lvalue
};

Теперь рассмотрим этот код:

// allowed: goes: D -> A(D)
A a((D()));

// compile failure. Goes:
// D -> A(D) -> A(E)
A a = D();

При инициализации копирования сначала преобразуется в A,Но затем временный объект A снова пытаются скопировать в конечный объект.Для этого потребуется способ, используя operator E.Но это еще одно преобразование, определенное пользователем при инициализации, которое стандарт запрещает:

13.3.3.1/4

При вызове для копирования временного объекта на втором шаге класса copy-инициализация, [...], разрешены только стандартные последовательности преобразования и последовательности преобразования эллипса.

Вот почему ваш код не выполняется.

0 голосов
/ 02 марта 2010

Дальнейшие исследования привели меня к этой заметке , что заставило меня поверить, что это известное ограничение реализации:

3 из тестов в настоящее время не для меня (не во время компиляции, должен скомпилировать, запустить и передать). Все это связано с конвертирующий конструктор, указанный в [unique.ptr.single.ctor]. когда источник и цель разного типа, эта эмуляция требует что преобразование должно быть явным, и отказывается компилировать по неявному преобразования:

unique_ptr<base> b(unique_ptr<derived>()); // ok

unique_ptr<base> b = unique_ptr<derived>(); // causes 3 compile time failures under unique.ptr/unique.ptr.single/unique.ptr.single.ctor .
...