Russian Qt Forum
Ноябрь 22, 2024, 04:26 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 ... 7 8 [9]   Вниз
  Печать  
Автор Тема: C++ Object Token Library  (Прочитано 58804 раз)
ssoft
Программист
*****
Offline Offline

Сообщений: 584


Просмотр профиля
« Ответ #120 : Февраль 20, 2020, 09:09 »

Свежак от Herb Sutter: Move, simply.
У Герба другое мнение Улыбающийся:

Конечно, хотелось бы видеть теоретическое обоснование, а не толкование стандарта. А так ... можно и поспорить))).
Хотя, где я, и где Herb  Смеющийся

Не будем не рассматривать возможные специализации функции move, возьмем только стандартную реализацию.
И пройдемся по некоторым положениям свежака).

Цитировать
If any non-const function on an object (including moving from it) makes the object invalid, the function has a bug.

Действительно, простое применение функции move никогда не делают никакой объект инвалидом). Можно хоть миллион раз вызвать move.
Работают здесь по настоящему либо конструктор, либо оператор перемещения (такие специальные для работы с rvalue значениями).

Цитировать
... C++ already uses move automatically when copying from an object it knows will never be used again, such as a temporary object or a local variable being returned or thrown from a function.

Как видно, критерием автоматического применения move является знание, что экземпляр объекта не используется снова. Как правило, это временные объекты, которые разрушаются сразу после перемещения. Использование move является принудительным преобразованием к виду rvalue, подобного временному объекту, который после операции перемещения применять в дальнейшем не предполагается.

Если разделить экземпляр объекта на две составляющие - значение и токен (то, посредством чего можно обратиться к значению), то можно заметить, что rvalue - это значение без токена, а lvalue с токеном. Логически операция move подразумевает перемещение значения из одного места в другое, и если токен (а не значение) после извлечения из него значения станет инвалидом в чем здесь проблема? Если от донора к акцептору пересадить сердце, останется ли жив донор?

Herb рассматривает пример с IndirectInt
Цитировать
Код
C++ (Qt)
// Buggy class: Move leaves behind a null smart pointer
class IndirectInt {
   shared_ptr<int> sp = make_shared<int>(42);
public:
   // ... more functions, but using defaulted move functions
   bool operator<(const IndirectInt& rhs) const { return *sp < *rhs.sp; }
                                               // oops: unconditional deref
   // ...
};
IndirectInt i[2];
i[0] = move(i[1]); // move leaves i[1].sp == nullptr
sort(begin(i), end(i)); // undefined behavior
 

Здесь поменяны местами причина и следствие. Утверждение, что класс некорректен следует из утверждения, что некорректным является рассмотренное поведение. Интересно, а что будет, если рассмотреть такой вариант?

Код:
double i[2];
i[1] = std::nan("0");
sort(begin(i), end(i)); // undefined behavior

Еще одно утверждение

Цитировать
... users won’t always know if a given object they encounter is moved-from. For example:
Код
C++ (Qt)
void f(const IndirectInt& a, const IndirectInt& b) {
   if (a < b)  // this would be a bug without first testing (somehow) that a and b both aren't moved-from
      // ...
}
 

не является правдой! Пользователь функции, вызывающий f( a, b ), всегда знает перемещались ли a или b до момента вызова. Разработчик же функции не обязан контролировать существование значений у неопциональных параметров.

Ну и корень всего)

Цитировать
Does the “moved-from” state correspond to the “partially formed but not well formed” described in Elements of Programming(aka EoP)?
Not quite.

In EoP, the description of an object’s state as “partially formed but not well formed” is similar to the C++ Standard’s description of “valid but unspecified.” The difference is that EoP requires such objects to be assignable and destroyable (i.e., partially formed) while the C++ standard makes a broader statement that “operations on the object behave as specified for its type” and that a moved-from object “must still meet the requirements of the library component that is using it.” (See Cpp17MoveConstructible and Cpp17MoveAssignable.)

Собственно, из такой формулировки стандарта делаются все выводы о некорректности той или иной реализации выше. При этом такая логическая модель перемещения (первичная по отношению к языку программирования) является противоречивой, в отличие от модели EoP. При этом пример not_null именно и показывает на практике противоречивость модели. Следовательно требуется корректировка стандарта. При этом многие пользователи языка и разработчики статических анализаторов сделали вывод о том, что лучшей практикой является считать, что перемещенный экземпляр объекта лучше не трогать).

А так это похоже на известный анекдот - "Ежики плакали и кололись, но продолжали жрать кактус".
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #121 : Февраль 20, 2020, 11:20 »

Пользователь функции, вызывающий f( a, b ), всегда знает перемещались ли a или b до момента вызова.

Вряд ли всё так просто. Ситуации с перемещением наверняка стоит рассматривать наравне с "common dangling", которые анализируются в "Lifetime safety: Preventing common dangling". Интересно, что в "Reply-to" этого документа числится... Herb Sutter Улыбающийся. Может ему надоело возиться с dangling pointers и не охота ещё и с перемещением заморачиваться, вот он и топит за то, чтоб после перемещения объекты были в валидном состоянии и не надо было диагностик для этого делать )).

По “valid but unspecified” наверное только ленивый не прошёлся Улыбающийся. Мне ещё интересно, как одновременно уживаются две мысли: "Moving from an object does not end its lifetime, only destruction does, so moving from an object does not make it invalid or not obey its invariants." и "relocation/destructive-move leaves an object that is guaranteed to be no longer used" or similar (in those proposals, including even its dtor won't be called)" Улыбающийся.

Вообще, хотелось бы получить какую-нибудь золотую середину: в общем случае к moved-from объекту обращаться нельзя, но в postcondition (когда Contracts доделают) можно указать, в каком состоянии окажется перемещённый объект, чтобы компилятор и анализаторы могли использовать эту информацию. Всё таки есть объекты, которые после перемещения могут переходить в определённое валидное состояние.
Записан

Пока сам не сделаешь...
ssoft
Программист
*****
Offline Offline

Сообщений: 584


Просмотр профиля
« Ответ #122 : Февраль 20, 2020, 13:54 »

Всё таки есть объекты, которые после перемещения могут переходить в определённое валидное состояние.

Исходя из требования модели или особенности технической реализации?
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #123 : Февраль 20, 2020, 18:29 »


Мне ещё интересно, как одновременно уживаются две мысли: "Moving from an object does not end its lifetime, only destruction does, so moving from an object does not make it invalid or not obey its invariants." и "relocation/destructive-move leaves an object that is guaranteed to be no longer used" or similar (in those proposals, including even its dtor won't be called)" Улыбающийся.


Move и "destructive move" таки разные вещи. Второй в природе не существует и народ обсуждает, а нужна ли она или стремно.
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #124 : Февраль 21, 2020, 10:51 »

Всё таки есть объекты, которые после перемещения могут переходить в определённое валидное состояние.

Исходя из требования модели или особенности технической реализации?

Одно другому не мешает Улыбающийся. Те же самые умные указатели std при перемещении "зануляются" в реализации, что соответствует переходу в "пустое" состояние для optional-like объекта. И выглядит это вполне логично:

Код
C++ (Qt)
#include <memory>
#include <iostream>
 
class Label
{
public:
   using Caption = std::unique_ptr<std::string>;
 
   Label() = default;
   explicit Label(Caption caption) noexcept
       : m_caption{std::move(caption)}
   {}
 
   Caption releaseCaption() noexcept
   { return std::move(m_caption); }
 
   void print() const
   {
       std::cout << "Caption: " << (m_caption ? *m_caption : "[none]")
                 << std::endl;
   }
 
private:
   Caption m_caption;
};
 
int main()
{
   Label label{std::make_unique<std::string>("Hello!")};
   label.print();
   label.releaseCaption();
   label.print();
}

Как будет выглядеть Label::releaseCaption(), если придётся вручную переинициализировать m_caption?
Записан

Пока сам не сделаешь...
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #125 : Февраль 21, 2020, 11:15 »

Move и "destructive move" таки разные вещи. Второй в природе не существует и народ обсуждает, а нужна ли она или стремно.

Что "destructive move" ещё в природе не существует, то я в курсе. Правда ещё не изучал внимательно, что там предлагают. Я всё пытаюсь расшифровать это высказывание Саттера:
Цитировать
Yes, an intended effect of this change is that a not_null<unique_ptr<T>> can only sit there, it can't be moved anywhere. But this is already inherently true, moving one of those is impossible today without breaking the not_null invariant. The correct long-term answer for this would be if C++ gets something along the lines of the relocation / destructive move semantics proposals, where roughly "relocation/destructive-move leaves an object that is guaranteed to be no longer used" or similar (in those proposals, including even its dtor won't be called), then that would naturally enable cases like returning a local not_null<unique_ptr<T>> by value.

С одной стороны запрещают перемещение not_null, чтобы он не оказался в невалидном состоянии, с другой он говорит, что "relocation/destructive move" чем-то поможет. Я явно что-то упускаю в логике Саттера Улыбающийся. Возможности только "returning a local not_null<unique_ptr<T>> by value", по-моему, маловато будет для полноценного использования not_null<unique>.
Записан

Пока сам не сделаешь...
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #126 : Июнь 29, 2020, 12:14 »

"C++ Move Semantics - The Complete Guide" now Feature Complete. Всё, что нужно знать про перемещение. Всего-то страниц 200 Улыбающийся.
Записан

Пока сам не сделаешь...
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #127 : Июнь 30, 2020, 11:02 »

А вообще нужна обёртка-токен над объектом-по-месту? Есть варианты использования, когда без неё тяжко?

Она нужна только для обеспечения однообразия интерфейса доступа к значению, чтобы вариант heap_* легко можно было заменить на inplace_* при необходимости.

Кстати, добавил в библиотеку raw::unique[_single] и raw::unique_optional для "inplace-объектов".
Записан

Пока сам не сделаешь...
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #128 : Август 19, 2020, 10:59 »

Про отношения между объектами в картинках: unique_ptr, shared_ptr, weak_ptr, or reference_wrapper for class relationships.
Записан

Пока сам не сделаешь...
Страниц: 1 ... 7 8 [9]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.213 секунд. Запросов: 23.