Здравствуйте, Аноним, Вы писали: А>Объясните пожалуйста, почему вот этот код не работает: А>В случае, когда везде В или А, или, если добавить "(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>
вспомним конструкторы:
компилятор должен выполнить конвертацию "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
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.
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.
Операторы преобразования в 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>)", то все нормально.
Здравствуйте, Аноним, Вы писали:
А>Объясните пожалуйста, почему вот этот код не работает:
А>
А>#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
Здравствуйте, abrarov, Вы писали:
A>Не понятно, что означает "не работает" (MSVC 10.0 — собирается и даже без warning-ов)?
Не работает, значит не компилируется.
A>Что сразу заметно: std::auto_ptr<A> должен удалять объект, на который он указывает, посредством вызова деструктора ~A(). Но так как этот деструктор невиртуальный, и реально p указывает на экземпляр B, то имеет место ошибка — при уничтожении p вместо вызова деструктора ~B() будет вызван ~A() (и только).
В теме по-моему написано о чем вопрос. Виртуальный деструктор тут оффтоп.
Вопрос такой, а вот эта реализация копирования auto_ptr через auto_ptr_ref (семантика ссылки, насколько помню) является единственной? Почему именно она выбрана? Ведь можно было сделать внутри mutable storage<X> st; У него были бы конструкторы и операторы присваивания принимающие неконстантные ссылки. А у объемлющего auto_ptr были бы обычные, с константными ссылками. Или тут дело в compile-time запрете на хранение auto_ptr в контейнерах? Спасибо.
Здравствуйте, Masterkent, Вы писали:
M>Но даже если бы и нашлось одно подходящее пользовательское преобразование, дальнейшая direct-инициализация (являющаяся частью copy-инициализации) не могла бы пройти успешно в силу ограничения 13.3.3.1/4
что-то не получилось воспроизвести этот случай http://ideone.com/PwEId
комо компилирует
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 *) невозможна
}
Здравствуйте, Masterkent, Вы писали:
M>Теперь давай вот так его переделаем:
данную ситуацию я обычно озвучиваю "требуется более одного неявного преобразования, поэтому не компилируется", даже если это не по стандарту