Защо observer_ptr не се нулира след ход?

Защо observer_ptr не е нулира след операция по преместване?

Той е правилно настроен на nullptr в конструкцията си по подразбиране и това има смисъл (и предотвратява насочването към боклук).

И, съответно, трябва да се нулира, когато std::move()'d от, точно както std::string, std::vector и т.н.

Това би го направило добър кандидат в няколко контекста, където необработените указатели имат смисъл, както и автоматично генериране на операции за преместване на класове с необработени членове на данни от указатели, като в този случай.


РЕДАКТИРАНЕ

Както @JonathanWakely посочи в коментарите (и това е свързано с гореспоменатия въпрос):

ако observer_ptr беше нула след преместване, може да се използва за прилагане на Правилото за нула за типове, които имат член-указател. Това е много полезна функция.


person Mr.C64    schedule 10.03.2014    source източник
comment
Защо трябва да се нулира? Не трябва да притежава предмета, към който сочи.   -  person juanchopanza    schedule 11.03.2014
comment
Струва повече да го нулираш, отколкото да не го направиш, и така или иначе не трябва да разчиташ на преместена стойност.   -  person Alan Stokes    schedule 11.03.2014
comment
Уау, observer_ptr изглежда като най-нелепото предложение, което съм виждал.   -  person user541686    schedule 11.03.2014
comment
@juanchopanza: Не притежава нещото, нали, всъщност не трябва да го ИЗТРИВА. Говоря само за задаване на стойността му на nullptr.   -  person Mr.C64    schedule 11.03.2014
comment
@AlanStokes: Напълно не съм съгласен. Преместеният обект трябва да е в безопасно състояние.   -  person Mr.C64    schedule 11.03.2014
comment
@AlanStokes, не можеш да кажеш това за всички типове. За някои типове като unique_ptr, unique_lock и future абсолютно трябва да разчитате на преместена стойност, в противен случай получавате множество собственици! За дефинирани от потребителя типове с непритежаващи указатели е много полезно да имате нулев тип тъп указател след преместване, вижте stackoverflow.com /a/22307926/981959   -  person Jonathan Wakely    schedule 11.03.2014
comment
Нулирането на преместено нещо предполага, че собствеността върху предмета е прехвърлена. Но няма собственост за прехвърляне, така че няма смисъл да се нулира.   -  person juanchopanza    schedule 11.03.2014
comment
@juanchopanza, ако observer_ptr беше null след движение, може да се използва за прилагане на правилото за нула за типове, които имат член-указател. Това е много полезна функция. Всеки тип, който има член-указател и прави нещо с него в деструктора, ако указателят е различен от нула, може да използва tidy_ptr (вж. unique_lock, подобни на ScopeGuard типове и т.н.)   -  person Jonathan Wakely    schedule 11.03.2014
comment
@JonathanWakely: точно!   -  person Mr.C64    schedule 11.03.2014
comment

Създавам електронна таблица за инвентаризация за малка компания с много много начинаещи потребители на Excel. Като такъв искам да използвам макроси и VBA, за да свърша по-голямата част от работата, така че да не се претоварват от необходимостта да редактират формула или да създават диаграми. В един идеален свят те биха научили някои отлични, но реалността е, че това няма да се случи.

Досега имам хубава потребителска форма, която улеснява въвеждането и след натискане на бутона за изпращане вмъква всички данни в списък със стоки на първия наличен ред. Искам обаче да сложа и бутон в края на всеки ред, който да натискат, ако даден артикул бъде продаден впоследствие. Написах макрос за това („Разпродажба“), който намалява количеството на артикула, премества подробностите за артикула в продаден лист и изтрива изцяло реда, ако количеството падне до нула. Проблемът ми е, че не мога да накарам бутона за изпращане на моята потребителска форма да добавя макро бутон към променлив номер на ред.

Ето какво имам досега:

    Dim LRow As Long
    Dim ws As Worksheet
    Dim rng As Range
    Set ws = Worksheets("Stock Warehouse")
    LRow = ws.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Row
    Set rng = ws.Cells(LRow, 23)

    With ws
    .Cells(LRow, 1).Value = Date
    .Cells(LRow, 2).Value = Me.CATEGORY.Value

     (---etc etc until---)

    .Cells(LRow, 21).FormulaR1C1 = "=Sum(rc[-1]/rc[-11])"

    rng.Select
    ws.Buttons.Add(rng.Left, rng.Top, rng.Width, rng.RowHeight).Select
    Selection.OnAction = "Sale"
    Selection.Characters.Text = "SALE"
    With Selection.Characters(Start:=1, Length:=4).Font
    .Name = "Lucida Grande"
    (---more formatting stuff---)
    End With

Ако го настроя да отиде до клетка с име (т.е. „W5“), той работи и вмъква бутона. Толкова проблематично е просто да го накараш да вмъкне бутон в LRow, 23! Вероятно е доста ясно, но се взирах в това от дни и не мога да го разбера.

  -  person Jonathan Wakely    schedule 11.03.2014
comment
@JonathanWakely Това би било полезно, но имах чувството, че не е предназначено за такива неща. От друга страна, не виждам голяма полза от него в сегашния му предложен вид.   -  person juanchopanza    schedule 11.03.2014
comment
@juanchopanza, текущата му употреба е да бъде изрично, че предавате непритежаващ указател, което е полезно, защото не е ясно какво възнамерява да прави void f(X*) с указателя (ще поеме ли собственост?) Това е полезно. Просто мисля, че би било по-полезно, ако имаше и едно допълнително свойство и беше null-after-move, в противен случай имам нужда от друг тъп интелигентен указател, който е абсолютно същият, освен че е null-after-move .   -  person Jonathan Wakely    schedule 11.03.2014
comment
@JonathanWakely Съгласен съм и разбирам мотивацията. Но с движението за нулиране не може да се използва в по-нататъшни глупави типове наблюдатели, които не изискват умни неща да се случват при преместване. Но може би нуждата от този супер тъп интелигентен показалец е по-малко належаща от тази, която описвате.   -  person juanchopanza    schedule 11.03.2014
comment
@juanchopanza, не мога да се сетя за полезни типове, които изискват указател да има старата си стойност след преместване, но ако искате това, използвайте вграден тип указател.   -  person Jonathan Wakely    schedule 11.03.2014
comment
@JonathanWakely Нито аз, но като се има предвид начина, по който нещата са обяснени в предложението, не бих очаквал да се нулира при движение. Всичко, което казвам е, че това поведение ми изглежда самосъгласувано, а не че е най-полезният тъп интелигентен показалец. Със сигурност ще имам повече полза от твоя tidy_ptr.   -  person juanchopanza    schedule 11.03.2014


Отговори (3)


Изглежда, че много хора първо пропускат смисъла и полезността на тази идея.

Обмисли:

template<typename Mutex>
class unique_lock
{
  Mutex* pm;

public:
  unique_lock() : pm() { }

  unique_lock(Mutex& m) : pm(&m) { }

  ~unique_lock() { if (pm) pm->unlock(); }

  unique_lock(unique_lock&& ul) : pm(ul.pm) { ul.pm = nullptr; }

  unique_lock& operator=(unique_lock&& ul)
  {
    unique_lock(std::move(ul)).swap(*this);
    return *this;
  }

  void swap(unique_lock& ul) { std::swap(pm, ul.pm); }
};

С "тъп" интелигентен указател, който е null-on-default-construction и null-after-move, можете да по подразбиране три от специалните членски функции, така че става:

template<typename Mutex>
class unique_lock
{
  tidy_ptr<Mutex> pm;

public:
  unique_lock() = default;                            // 1

  unique_lock(Mutex& m) : pm(&m) { }

  ~unique_lock() { if (pm) pm->unlock(); }

  unique_lock(unique_lock&& ul) = default;            // 2

  unique_lock& operator=(unique_lock&& ul) = default; // 3

  void swap(unique_lock& ul) { std::swap(pm, ul.pm); }
};

Ето защо е полезно да имате тъп, непритежаващ интелигентен указател, който е нулев след преместване, като tidy_ptr

Но observer_ptr е само null-on-default-construction, така че ако е стандартизирано, ще бъде полезно за деклариране на функция, която да вземе непритежаващ указател, но няма да е полезно за класове като този по-горе, така че аз все още ще има нужда от друг непритежаващ тъп тип показалец. Да имаш два типа тъпи интелигентни показалци, които не притежават, изглежда почти по-лошо, отколкото да нямаш нито един!

person Jonathan Wakely    schedule 10.03.2014
comment
Отбелязах това като отговор. Изглежда, че настоящото предложение на observer_ptr не взе под внимание полезните аспекти, които посочихте тук. Засега не мога да намеря никаква валидна обосновка за настоящото предложение observer_ptr. - person Mr.C64; 11.03.2014
comment
Предложението описва обосновката. - person Jonathan Wakely; 11.03.2014

И така, конструкторите за преместване са предназначени да направят конструкторите за копиране по-евтини в определени случаи.

Нека напишем какви очакваме да бъдат тези конструктори и деструктори: (Това е малко опростено, но е добре за този пример).

observer_ptr() {
    this->ptr == nullptr;
}

observer_ptr(T *obj) {
    this->ptr = obj;
}

observer_ptr(observer_ptr<T> const & obj) {
    this->ptr = obj.ptr;
}

~observer_ptr() {
}

Вие предполагате, че класът предоставя конструктор за преместване, който изглежда така:

observer_ptr(observer_ptr<T> && obj) {
    this->ptr = obj.ptr;
    obj.ptr = null;
}

Когато предполагам, че съществуващият конструктор за копиране ще работи добре такъв, какъвто е, и е по-евтин от предложения конструктор за преместване.

Какво ще кажете за std::vector обаче?

std::vector, когато се копира, всъщност копира масива, който поддържа. И така, конструкторът за копиране на std::vector изглежда нещо като:

vector(vector<T> const & obj) {
    for (auto const & elem : obj)
        this->push_back(elem);
}

Конструкторът за преместване за std::vector може да оптимизира това. Може да направи това, защото тази памет може да бъде открадната от obj. В std::vector това всъщност е полезно да се направи.

vector(vector<T> && obj) {
    this->data_ptr = obj.data_ptr;
    obj.data_ptr = nullptr;
    obj.size = 0;
}
person Bill Lynch    schedule 10.03.2014
comment
+1, въпреки че предлагам да използвате nullptr вместо null в C++11. - person Marc Claesen; 11.03.2014
comment
Освен евтиността или не на операцията за преместване, тя не би имала смисъл семантично. Observer_ptr е точно това. То не се интересува и не знае за продължителността на живота на точката. Съществува само за целите на документацията. - person juanchopanza; 11.03.2014
comment
@juanchopanza Не биха ли дали тези свойства да го направят по-добър std::weak_ptr<T>? - person πάντα ῥεῖ; 11.03.2014
comment
@πάνταῥεῖ std::weak_ptr е твърде умен: позволява ви да проверите валидността и да получите споделена собственост. - person juanchopanza; 11.03.2014
comment
@juanchopanza 'std::weak_ptr е твърде умен', което всъщност ме разсмя :-D ... - person πάντα ῥεῖ; 11.03.2014
comment
И така, конструкторите за преместване са предназначени да направят конструкторите за копиране по-евтини в определени случаи. Това е една употреба, но не единствената. Те също така позволяват неща, които не могат да се копират, да бъдат предавани по стойност, напр. std::thread, std::unique_ptr, std::future и т.н. и всички тези неща разчитат да бъдат празни след движение. - person Jonathan Wakely; 11.03.2014

Освен незначителното въздействие върху производителността от нулирането на moved-from observer_ptr, което лесно се заобикаля (копиране вместо преместване), основната обосновка вероятно е да се имитира поведението на обикновените указатели възможно най-близо, следвайки принцип на най-малката изненада.

Има обаче потенциално много по-значим проблем с производителността: настройката по подразбиране на функциите за преместване позволява observer_ptr да бъде тривиално копируем. Тривиалната възможност за копиране позволява обект да бъде копиран с помощта на std::memcpy. Ако observer_ptr не можеше да се копира тривиално, нито класове с observer_ptr член на данните, което води до понижаване на производителността, което се спуска каскадно надолу по йерархията на композиционния клас.

Нямам представа какви подобрения на производителността могат да бъдат постигнати чрез използване на оптимизацията std::memcpy, но те вероятно са по-значими от гореспоменатия незначителен проблем с производителността.

person Joseph Thomson    schedule 09.03.2016