Есть ли в D что-то похожее на семантику перемещения C++0x?

Проблема «типов значений» с внешними ресурсами (такими как std::vector<T> или std::string) заключается в том, что их копирование, как правило, довольно дорого, а копии создаются неявно в различных контекстах, поэтому это, как правило, влияет на производительность. Ответ C++0x на эту проблему — семантика перемещения, которая концептуально основана на идее кражи ресурсов и технически основана на ссылках rvalue.

Есть ли в D что-то похожее на семантику перемещения или ссылки на rvalue?


person fredoverflow    schedule 16.11.2010    source источник


Ответы (5)


Я считаю, что в D есть несколько мест (например, возвращаемые структуры), где D удается заставить их перемещаться, тогда как C++ сделал бы их копиями. IIRC, компилятор будет выполнять перемещение, а не копирование в любом случае, когда он может определить, что копия не нужна, поэтому копирование структуры будет происходить реже в D, чем в C++. И, конечно же, поскольку классы являются ссылками, у них вообще нет проблемы.

Но несмотря на это, конструкция копирования уже работает в D иначе, чем в C++. Как правило, вместо объявления конструктора копирования вы объявляете конструктор постблита: this(this). Он выполняет полный memcpy перед вызовом this(this), и вы вносите только те изменения, которые необходимы для обеспечения того, чтобы новая структура была отделена от исходной (например, при необходимости выполнялась глубокая копия переменных-членов), в отличие от создания совершенно новой структуры. конструктор, который должен все скопировать. Итак, общий подход уже немного отличается от C++. Также общепризнанно, что структуры не должны иметь дорогостоящих конструкторов постблитов - копирование структур должно быть дешевым - так что это не такая проблема, как в C++. Объекты, которые было бы дорого копировать, обычно представляют собой либо классы, либо структуры со ссылкой или семантикой COW.

Контейнеры, как правило, являются ссылочными типами (в Phobos это структуры, а не классы, поскольку им не нужен полиморфизм, но их копирование не копирует их содержимое, поэтому они по-прежнему являются ссылочными типами), поэтому их копирование не требует больших затрат. как это было бы в C++.

В D вполне могут быть случаи, когда он мог бы использовать что-то похожее на конструктор перемещения, но в целом D был разработан таким образом, чтобы уменьшить проблемы, которые C++ имеет с копированием объектов вокруг, так что это далеко не проблема. что это на С++.

person Jonathan M Davis    schedule 17.11.2010
comment
Разве постблит не специфичен для D2? Я думаю, было бы неплохо уточнить, какую версию D мы сейчас обсуждаем :) - person Kos; 17.11.2010
comment
@Kos Я не знаю, что делает D1, а чего нет. Я использую только D2 и обычно предполагаю, что вопрос о D относится к D2, если не указано иное, особенно когда вопрос исходит от кого-то, кто не является пользователем D и может даже не знать, что существует версия 1 и версия 2 языка. Я просто отношусь к термину D как к текущей версии языка. - person Jonathan M Davis; 17.11.2010
comment
Но D2 был... ну, вроде C++0x-ish в прошлый раз, когда я проверял, то есть спецификация постоянно менялась, а поддержка компилятора была несколько экспериментальной? Это приводит меня к выводу, что нужно уточнить, говорит ли он о стабильной или экспериментальной версии или о языке. Или, может быть, D2 наконец стал стабильным? Я не в курсе D :-) - person Kos; 17.11.2010
comment
С выпуском Языка программирования D Андрея Александреску, D2 как язык стал по существу стабильным - можно вносить небольшие изменения, но ничего серьезного. Компилятор все еще нуждается в доработке (хотя многие ошибки применимы и к D1), и стандартная библиотека находится в стадии интенсивной разработки, но спецификация языка больше не находится в активной разработке. Этот вопрос охватывал ситуацию, сложившуюся несколько месяцев назад, и ситуация практически не изменилась: the-d-programming-language" title="Каковы текущие проблемы языка программирования d"> stackoverflow.com/questions/3205509/ - person Jonathan M Davis; 17.11.2010
comment
Я не могу понять третий абзац в этом ответе. Вы говорите: Контейнеры обычно являются ссылочными типами (в Phobos это структуры, а не классы). Как структура может быть ссылочным типом? Разве не должно быть наоборот? - person Nordlöw; 31.10.2012
comment
@Nordlow Structs могут быть ссылочными типами, если их копирование не приводит к глубокому копированию. Например, если структура содержит указатель или ссылку на класс в качестве переменной-члена и не объявляет конструктор постблита, то каждая копия этой структуры будет указывать на одни и те же данные. std.typecons.RefCounted будет примером этого, как и любой контейнер в std.container, который является структурой. - person Jonathan M Davis; 31.10.2012
comment
Чтобы немного расширить этот ответ, я думаю, что фундаментальное отличие состоит в том, что реализации D разрешено перемещать структуры в памяти без вызова какого-либо конструктора; ваша структура просто не должна содержать указатель на себя. D также разрешено копировать их, и в этом случае он вызывает постблит только для того, чтобы вы могли разрешать любые теперь общие ссылки внутри структуры, но постблит не для других целей, таких как исправление указателей (у вас недостаточно информации). Итак, D имеет семантику перемещения и копирования; он просто ВСЕГДА указывает конструктор перемещения для вас. :) (Поправьте меня если я ошибаюсь) - person entheh; 19.05.2013
comment
Я до сих пор не понимаю параграф 3. Во-вторых, конечно, метод D, заключающийся в том, что сначала выполняется поверхностное копирование, а затем вы вручную настраиваете все, что требует глубокого копирования, ошибочен в том смысле, что он неэффективен? Если у вас есть указатель на какие-то данные, и вы сначала мелко копируете его, только потом выделяете память и меняете его значение? Там есть 2 записи в указатель, когда вам нужна была только 1? - person FreelanceConsultant; 24.08.2015
comment
@user3728501 user3728501 Я не знаю, что еще сказать о абзаце 3. Копирование объекта класса — это просто копирование ссылки. Таким образом, это в основном то же самое, что копирование указателя. Копирование структуры, которая является ссылочным типом (например, она просто содержит указатель), будет таким же или, возможно, немного более дорогим, если оно включает подсчет ссылок. Контейнеры D являются ссылочными типами (будь то структуры или классы), поэтому их копирование обходится дешево. C++, с другой стороны, делает так, что его контейнеры являются типами значений, поэтому их передача приводит к глубоким копиям, что дорого. - person Jonathan M Davis; 25.08.2015
comment
@user3728501 user3728501 Что касается эффективности постблита, да, может быть немного менее эффективно делать поверхностную копию, а затем глубоко копировать определенные элементы, но вы говорите о переназначении одного указателя, когда вы выделяете память для выполнения глубокой копии, так что стоимость затмевается глубокой копией. Основное преимущество заключается в том, что вам не нужно явно копировать каждый элемент, как в C++. Таким образом, это уменьшает количество кода и помогает с правильностью. Основным недостатком является то, что в настоящее время он не работает с const или immutable, поскольку posblit изменяет переменные-члены. Однако у большинства структур нет постблитов. - person Jonathan M Davis; 25.08.2015
comment
@JonathanMDavis Спасибо за информацию ... Хотя мне действительно не нравится эта неэффективность. - person FreelanceConsultant; 25.08.2015

Я думаю, что все ответы полностью не ответили на исходный вопрос.

Во-первых, как указано выше, вопрос актуален только для структур. Классы не имеют осмысленного хода. Также указано выше, что для структур определенное количество перемещений будет происходить автоматически компилятором при определенных условиях.

Если вы хотите получить контроль над операциями перемещения, вот что вам нужно сделать. Вы можете отключить копирование, аннотировав это (это) с помощью @disable. Затем вы можете переопределить constructor(constructor &&that) C++, определив this(Struct that). Точно так же вы можете переопределить назначение с помощью opAssign(Struct that). В обоих случаях вам нужно убедиться, что вы уничтожили значения that.

Для присваивания, поскольку вам также нужно уничтожить старое значение this, самый простой способ — поменять их местами. Таким образом, реализация unique_ptr C++ будет выглядеть примерно так:

struct UniquePtr(T) {
    private T* ptr = null;

    @disable this(this); // This disables both copy construction and opAssign

    // The obvious constructor, destructor and accessor
    this(T* ptr) {
        if(ptr !is null)
            this.ptr = ptr;
    }

    ~this() {
        freeMemory(ptr);
    }

    inout(T)* get() inout {
        return ptr;
    }

    // Move operations
    this(UniquePtr!T that) {
        this.ptr = that.ptr;
        that.ptr = null;
    }

    ref UniquePtr!T opAssign(UniquePtr!T that) { // Notice no "ref" on "that"
        swap(this.ptr, that.ptr); // We change it anyways, because it's a temporary
        return this;
    }
}

Изменить: обратите внимание, что я не определял opAssign(ref UniquePtr!T that). Это оператор присваивания копии, и если вы попытаетесь определить его, компилятор выдаст ошибку, потому что вы объявили в строке @disable, что у вас нет такой вещи.

person Shachar Shemesh    schedule 31.01.2016
comment
Ваш код обсуждается здесь, если вы заинтересованы forum.dlang.org/post /[email protected] - person Maik Klein; 31.01.2016

D имеют отдельную семантику значения и объекта:

  • если вы объявите свой тип как struct, он будет иметь семантическое значение по умолчанию
  • если вы объявите свой тип как class, он будет иметь объектную семантику.

Теперь, предполагая, что вы не управляете памятью самостоятельно, как это делается по умолчанию в D — используя сборщик мусора — вы должны понимать, что объекты типов, объявленных как class, автоматически являются указателями (или «ссылками», если хотите) на реальный объект, а не сам реальный объект.

Итак, при передаче векторов в D вы передаете ссылку/указатель. Автоматически. Копия не задействована (кроме копии ссылки).

Вот почему D, C#, Java и другие языки не «нуждаются» в движущейся семантике (поскольку большинство типов являются семантикой объекта и управляются по ссылке, а не по копированию).

Может быть, они могли бы реализовать это, я не уверен. Но действительно ли они получат прирост производительности, как в C++? По природе это маловероятно.

person Klaim    schedule 16.11.2010
comment
Я знаю разницу между struct и class. Я хочу знать, могу ли я разработать struct с семантикой перемещения :) - person fredoverflow; 17.11.2010
comment
О, я вижу, вы хотите более точный ответ. Я должен буду проверить это или дождаться лучшего ответа. - person Klaim; 17.11.2010
comment
struct не имеет полноценную семантику (это подразумевает неизменяемость), это только передача по значению. - person Erik van Velzen; 01.04.2016

У меня почему-то возникает ощущение, что на самом деле ссылки на rvalue и вся концепция «семантики перемещения» являются следствием того, что в С++ нормально создавать локальные «временные» объекты стека. В D и большинстве языков сборщика мусора чаще всего объекты находятся в куче, а затем нет накладных расходов, связанных с копированием (или перемещением) временного объекта несколько раз при его возврате через стек вызовов, поэтому нет необходимости в механизме, чтобы избежать этих накладных расходов.

В D (и большинстве языков GC) объект class никогда не копируется неявно, и большую часть времени вы только передаете ссылку, поэтому это может означать, что вам не нужны ссылки на rvalue для их.

OTOH, объекты struct НЕ должны быть «дескрипторами ресурсов», а должны быть простыми типами значений, ведущими себя аналогично встроенным типам - так что, опять же, здесь нет причин для какой-либо семантики перемещения, ИМХО.

Это приведет к выводу, что у D нет ссылок на rvalue, потому что они ему не нужны.

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

person Kos    schedule 17.11.2010
comment
Следуя этой логике, вы также можете сказать, что C++ не нуждается в rvalue refs, потому что есть shared_ptrs. - person Viktor Sehr; 11.03.2011

Я думаю, если вам нужен источник, чтобы потерять ресурс, у вас могут быть проблемы. Однако, получив GC, вы часто можете избежать необходимости беспокоиться о нескольких владельцах, поэтому в большинстве случаев это может не быть проблемой.

person BCS    schedule 17.11.2010