Операторы преобразования в std::auto_ptr
От: Аноним  
Дата: 06.08.11 10:12
Оценка:
Объясните пожалуйста, почему вот этот код не работает:

#include <memory>

struct A {};
struct B : A {};

std::auto_ptr<B> foo()
{
    return std::auto_ptr<B>(new B);
}

int main()
{
    std::auto_ptr<A> p = foo();
}


В случае, когда везде В или А, или, если добавить "(std::auto_ptr<A>)", то все нормально.
Re: Операторы преобразования в std::auto_ptr
От: abrarov Россия http://asio-samples.blogspot.com/
Дата: 06.08.11 11:07
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Объясните пожалуйста, почему вот этот код не работает:


А>
А>#include <memory>

А>struct A {};
А>struct B : A {};

А>std::auto_ptr<B> foo()
А>{
А>    return std::auto_ptr<B>(new B);
А>}

А>int main()
А>{
А>    std::auto_ptr<A> p = foo();
А>}
А>


А>В случае, когда везде В или А, или, если добавить "(std::auto_ptr<A>)", то все нормально.


Не понятно, что означает "не работает" (MSVC 10.0 — собирается и даже без warning-ов)?
Что сразу заметно: std::auto_ptr<A> должен удалять объект, на который он указывает, посредством вызова деструктора ~A(). Но так как этот деструктор невиртуальный, и реально p указывает на экземпляр B, то имеет место ошибка — при уничтожении p вместо вызова деструктора ~B() будет вызван ~A() (и только).
Programs must be written for people to read, and only incidentally for machines to execute
Re[2]: Операторы преобразования в std::auto_ptr
От: mp_op  
Дата: 06.08.11 11:51
Оценка:
Здравствуйте, abrarov, Вы писали:

A>Не понятно, что означает "не работает" (MSVC 10.0 — собирается и даже без warning-ов)?

Не работает, значит не компилируется.

A>Что сразу заметно: std::auto_ptr<A> должен удалять объект, на который он указывает, посредством вызова деструктора ~A(). Но так как этот деструктор невиртуальный, и реально p указывает на экземпляр B, то имеет место ошибка — при уничтожении p вместо вызова деструктора ~B() будет вызван ~A() (и только).

В теме по-моему написано о чем вопрос. Виртуальный деструктор тут оффтоп.

Вот пример с ошибкой.
Re: Операторы преобразования в std::auto_ptr
От: uzhas Ниоткуда  
Дата: 06.08.11 12:46
Оценка: 5 (2)
Здравствуйте, Аноним, Вы писали:

А>Объясните пожалуйста, почему вот этот код не работает:

А>В случае, когда везде В или А, или, если добавить "(std::auto_ptr<A>)", то все нормально.
вот еще один рабочий вариант:
#include <memory>

struct A {};
struct B : A {};

std::auto_ptr<B> foo()
{
    return std::auto_ptr<B>(new B);
}

int main()
{
    std::auto_ptr<A> p(foo());
}

в этом случае происходит direct-initialization. при этом происходит одно неявное преобразование к auto_ptr_ref<B>
вспомним конструкторы:
  Скрытый текст
// 20.4.5.1 construct/copy/destroy:
explicit auto_ptr(X* p =0) throw();
auto_ptr(auto_ptr&) throw();
template<class Y> auto_ptr(auto_ptr<Y>&) throw(); *
auto_ptr& operator=(auto_ptr&) throw();
template<class Y> auto_ptr& operator=(auto_ptr<Y>&) throw();
auto_ptr& operator=(auto_ptr_ref<X> r) throw();
˜auto_ptr() throw();
// 20.4.5.3 conversions:
auto_ptr(auto_ptr_ref<X>) throw(); ***
template<class Y> operator auto_ptr_ref<Y>() throw();
template<class Y> operator auto_ptr<Y>() throw(); **


чтобы заработал ваш copy initialization
std::auto_ptr<A> p = foo();

компилятор должен выполнить конвертацию "foo()" в std::auto_ptr<A> и затем выполнить direct-initialization (при этом позволяется компилятору соптимизировать и выполнить только direct-initialization, но он обязан проверить возможность проведения полного сценария с конвертацией и инициализацией)
чтобы сконвертировать foo() в auto_ptr<A> уже нельзя воспользоваться шаблонным конструктором (*), т.к. foo() является lvalue (временный объект возвращается)
еще можно было бы воспользоваться оператором приведения (**), но и теперь нам нельзя воспользоваться конструктором (*), т.к. снова имеем lvalue, тогда можно было дополнительно применить конвертацию их auto_ptr<A> в auto_ptr_ref<A>, из которого легко конструируется auto_ptr<A> конструктором (***). В итоге мы получили более одного неявного приведения, а это делать запрещено.
в итоге нет вариантов, поэтому код и не компилируется
вот интересную статейку нашел на эту тему: http://stackoverflow.com/questions/4801136/c-copy-initialization-direct-initialization-the-weird-case
Re[2]: Операторы преобразования в std::auto_ptr
От: Masterkent  
Дата: 06.08.11 13:13
Оценка: 2 (1) +1
uzhas:

U>компилятор должен выполнить конвертацию "foo()" в std::auto_ptr<A> и затем выполнить direct-initialization (при этом позволяется компилятору соптимизировать и выполнить только direct-initialization


Вот direct-initialization, являющаяся частью copy-initialization, в отличие от последовательности преобразований, как раз-таки и может быть устранена с изменением observable behavior

U>чтобы сконвертировать foo() в auto_ptr<A> уже нельзя воспользоваться шаблонным конструктором (*), т.к. foo() является lvalue (временный объект возвращается)


foo() — rvalue

U>еще можно было бы воспользоваться оператором приведения (**), но и теперь нам нельзя воспользоваться конструктором (*), т.к. снова имеем lvalue, тогда можно было дополнительно применить конвертацию их auto_ptr<A> в auto_ptr_ref<A>, из которого легко конструируется auto_ptr<A> конструктором (***). В итоге мы получили более одного неявного приведения, а это делать запрещено.


Но даже если бы и нашлось одно подходящее пользовательское преобразование, дальнейшая direct-инициализация (являющаяся частью copy-инициализации) не могла бы пройти успешно в силу ограничения 13.3.3.1/4

However, when considering the argument of a user-defined conversion function that is a candidate by 13.3.1.3 when invoked for the copying of the temporary in the second step of a class copy-initialization, or by 13.3.1.4, 13.3.1.5, or 13.3.1.6 in all cases, only standard conversion sequences and ellipsis conversion sequences are allowed.

Re[2]: Операторы преобразования в std::auto_ptr
От: mp_op  
Дата: 06.08.11 13:49
Оценка:
Здравствуйте, uzhas.

Спасибо за развернутый ответ, теперь все понятно.

Вопрос такой, а вот эта реализация копирования auto_ptr через auto_ptr_ref (семантика ссылки, насколько помню) является единственной? Почему именно она выбрана? Ведь можно было сделать внутри mutable storage<X> st; У него были бы конструкторы и операторы присваивания принимающие неконстантные ссылки. А у объемлющего auto_ptr были бы обычные, с константными ссылками. Или тут дело в compile-time запрете на хранение auto_ptr в контейнерах? Спасибо.
Re[3]: Операторы преобразования в std::auto_ptr
От: uzhas Ниоткуда  
Дата: 06.08.11 14:39
Оценка:
Здравствуйте, Masterkent, Вы писали:

M>foo() — rvalue

вот я балбес
Re[3]: Операторы преобразования в std::auto_ptr
От: uzhas Ниоткуда  
Дата: 06.08.11 15:04
Оценка:
Здравствуйте, Masterkent, Вы писали:

M>Но даже если бы и нашлось одно подходящее пользовательское преобразование, дальнейшая direct-инициализация (являющаяся частью copy-инициализации) не могла бы пройти успешно в силу ограничения 13.3.3.1/4

что-то не получилось воспроизвести этот случай
http://ideone.com/PwEId
комо компилирует
Re[3]: Операторы преобразования в std::auto_ptr
От: Masterkent  
Дата: 06.08.11 17:00
Оценка: 2 (1)
mp_op:

_>Вопрос такой, а вот эта реализация копирования auto_ptr через auto_ptr_ref (семантика ссылки, насколько помню) является единственной?


Это, скорее, не копирование, а перемещение. Использование std::auto_ptr_ref для реализации перемещения от rvalue std::auto_ptr требуется стандартом C++.

_>Почему именно она выбрана? Ведь можно было сделать внутри mutable storage<X> st; У него были бы конструкторы и операторы присваивания принимающие неконстантные ссылки. А у объемлющего auto_ptr были бы обычные, с константными ссылками.


Тогда была бы возможность изменять логически константные объекты auto_ptr, что выглядело бы несколько странно. По-хорошему, кстати, следовало бы запретить использование функций auto_ptr(auto_ptr &) и auto_ptr::operator =(auto_ptr &), и передачу владения осуществлять через явный move, возвращающий rvalue типа auto_ptr или auto_ptr_ref.
Re[4]: Операторы преобразования в std::auto_ptr
От: Masterkent  
Дата: 06.08.11 17:02
Оценка:
uzhas:

M>>Но даже если бы и нашлось одно подходящее пользовательское преобразование, дальнейшая direct-инициализация (являющаяся частью copy-инициализации) не могла бы пройти успешно в силу ограничения 13.3.3.1/4

U>что-то не получилось воспроизвести этот случай
U>http://ideone.com/PwEId
U>комо компилирует

Разумеется, там же у тебя класс A имеет обычный copy-конструктор, который целиком выполняет "the second step of a class copy-initialization" без всяких вспомогательных преобразований. Теперь давай вот так его переделаем:

struct A
{
    A() {}
    A(A &);
    A(A *) {}
    operator A *() { return this; }
};

struct B
{
    operator A() const { return A(); }
};

int main()
{
    A a1 = A(); // OK
    A a2 = B(); // ошибка: инициализация при помощи A(A *) невозможна
}
Re[5]: Операторы преобразования в std::auto_ptr
От: uzhas Ниоткуда  
Дата: 06.08.11 17:35
Оценка:
Здравствуйте, Masterkent, Вы писали:

M>Теперь давай вот так его переделаем:

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