Подтвердить что ты не робот

Имеет ли D нечто похожее на семантику перемещения С++ 0x?

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

Имеет ли D что-либо подобное перемещению семантики или ссылки rvalue?

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

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

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

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

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

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

Ответ 3

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

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

OTOH, struct объекты НЕ должны быть "ручками к ресурсам", но простые типы значений, которые ведут себя подобно встроенным типам - так что опять-таки нет причин для какой-либо семантики перемещения здесь, IMHO.

Это дало бы заключение - D не имеет rvalue ref, потому что он им не нужен.

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

Ответ 4

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

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

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

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

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 a temporary
        return this;
    }
}

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

Ответ 5

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