سؤال

مشكلة "أنواع القيمة" مع الموارد الخارجية (مثل std::vector<T> أو std::string) هو أن نسخها يميل إلى أن يكون مكلفًا للغاية ، ويتم إنشاء نسخ ضمنيًا في سياقات مختلفة ، لذلك يميل هذا إلى أن يكون مصدر قلق للأداء. إجابة C ++ 0x على هذه المشكلة نقل الدلالات, ، الذي يعتمد بشكل مفهوم على فكرة تبرئة الموارد وتشغيله تقنيًا المراجع RVALUE.

هل لدى D أي شيء مشابه لنقل الدلالات أو المراجع RValue؟

هل كانت مفيدة؟

المحلول

أعتقد أن هناك عدة أماكن في D (مثل الهياكل العائدة) التي تمكنت من تحقيقها في حين أن C ++ سيجعلها نسخة. IIRC ، سيقوم برنامج التحويل البرمجي بحركة بدلاً من نسخة في أي حال من الأحوال حيث يمكن أن تحدد أن النسخة غير مطلوبة ، لذلك سيحدث نسخ الهيكل أقل في D من C ++. وبالطبع ، نظرًا لأن الفصول الدراسية هي مراجع ، فهي لا تواجه مشكلة على الإطلاق.

ولكن بغض النظر ، فإن Copy Construction يعمل بالفعل بشكل مختلف في D عن C ++. بشكل عام ، بدلاً من الإعلان عن مُنشئ النسخ ، تعلن عن مُنشئ ما بعد البليت: this(this). إنها تفعل memcpy كاملة من قبل this(this) يُطلق عليه ، ولا تجري سوى أي تغييرات ضرورية للتأكد من أن البنية الجديدة منفصلة عن الأصل (مثل القيام بنسخة عميقة من متغيرات الأعضاء عند الحاجة) ، بدلاً من إنشاء مُنشئ جديد تمامًا يجب أن ينسخ كل شيء. لذلك ، فإن النهج العام يختلف بالفعل بعض الشيء عن C ++. من المتفق عليه عمومًا أنه لا ينبغي أن يكون للهياكل منشئات ما بعد البليت باهظة الثمن - يجب أن تكون بنيات نسخ رخيصة - لذلك فهي أقل من مشكلة مما ستكون عليه في C ++. الكائنات التي ستكون باهظة الثمن للنسخ هي عمومًا فئات أو هياكل مع مرجعية أو دلالات بقرة.

الحاويات عبارة عن أنواع مرجعية عمومًا (في Phobos ، هي هياكل بدلاً من الفصول الدراسية ، لأنها لا تحتاج إلى تعدد الأشكال ، لكن نسخها لا ينسخ محتوياتها ، لذلك لا تزال أنواع مرجعية) ، لذا فإن نسخها غير مكلف كما سيكون في C ++.

قد تكون هناك حالات في D حيث يمكن أن تستخدم شيئًا مشابهًا لمؤسسة Move ، ولكن بشكل عام ، تم تصميم D بطريقة تقلل من المشكلات التي يواجهها C ++ مع نسخ الكائنات حولها ، لذلك ليس في أي مكان بالقرب من المشكلة أنه في C ++.

نصائح أخرى

D لها قيمة منفصلة ودلالات الكائنات:

  • إذا أعلنت نوعك كما struct, ، سيكون له قيمة الدلالي بشكل افتراضي
  • إذا أعلنت نوعك كما class, ، سيكون لها كائن الدلالي.

الآن ، على افتراض أنك لا تدير الذاكرة بنفسك ، لأنها الحالة الافتراضية في D - باستخدام جامع القمامة - عليك أن تفهم هذا الكائن من الأنواع المعلنة باسم class هي مؤشرات تلقائي (أو "مرجع" إذا كنت تفضل) للكائن الحقيقي ، وليس الكائن الحقيقي نفسه.

لذلك ، عند تمرير المتجهات حول D ، ما تمريره هو المرجع/المؤشر. تلقائيا. لا توجد نسخة متورطة (بخلاف نسخة المرجع).

لهذا السبب لا "تحتاج" D ، C#، Java وغيرها من اللغة الدلالية (لأن معظم الأنواع هي الكائن الدلالي ويتم معالجتها بالرجوع إليها ، وليس عن طريق النسخة).

ربما يمكنهم تنفيذه ، لست متأكدًا. ولكن هل سيحصلون حقًا على زيادة الأداء كما في C ++؟ بطبيعتها ، لا يبدو من المحتمل.

لدي بطريقة ما أن الشعور بأن المراجع RVALUE والمفهوم الكامل لـ "Move Disantics" هو نتيجة أن يكون من الطبيعي في C ++ إنشاء كائنات محلية "مؤقتة". في لغات D ومعظم لغات GC ، من الأكثر شيوعًا أن يكون لديك كائنات على الكومة ، ثم لا يوجد أي عام مع نسخ كائن مؤقت (أو نقل) عدة مرات عند إرجاعه من خلال مكدس مكالمة - لذلك ليست هناك حاجة لآلية لتجنب تلك النفقات العامة أيضًا.

في D (ومعظم لغات GC) أ class لا يتم نسخ الكائن أبدًا بشكل ضمني وأنت تمر فقط بالمرجع في معظم الوقت ، لذلك هذا مايو يعني أنك لا تحتاج إلى أي مراجع RValue لهم.

otoh ، struct ليس من المفترض أن تكون الكائنات "مقابضًا للموارد" ، ولكن أنواع القيمة البسيطة التي تتصرف على غرار الأنواع المبنية - لذلك مرة أخرى ، لا يوجد سبب لأي خطوة دلالات هنا ، IMHO.

هذا من شأنه أن ينتج عنه استنتاج - لا يحتوي D على RValue Refs لأنه لا يحتاج إليها.

ومع ذلك ، لم أستخدم مراجع RVALUE في الممارسة العملية ، لم أقرأها فقط ، لذلك ربما تكون قد تخطيت بعض حالات الاستخدام الفعلي لهذه الميزة. يرجى التعامل مع هذا المنشور باعتباره مجموعة من الأفكار حول هذا الموضوع والتي نأمل أن تكون مفيدة لك ، وليس كحكمة موثوقة.

أعتقد أن جميع الإجابات فشلت تمامًا في الإجابة على السؤال الأصلي.

أولاً ، كما هو مذكور أعلاه ، يكون السؤال ذا صلة فقط بالهياكل. الفصول ليس لها خطوة ذات مغزى. كما ذكر أعلاه ، بالنسبة للهياكل ، سيحدث كمية معينة من التحرك تلقائيًا بواسطة المترجم في ظل ظروف معينة.

إذا كنت ترغب في السيطرة على عمليات الحركة ، فإليك ما عليك القيام به. يمكنك تعطيل النسخ عن طريق التعليق على هذا (هذا) مع disable. بعد ذلك ، يمكنك تجاوز C ++ constructor(constructor &&that) عن طريق التعريف this(Struct that). وبالمثل ، يمكنك تجاوز التعيين مع opAssign(Struct that). في كلتا الحالتين ، تحتاج إلى التأكد من تدمير قيم that.

للواجب ، نظرًا لأنك تحتاج أيضًا إلى تدمير القيمة القديمة this, ، أبسط طريقة هي مبادلة لهم. تنفيذ C ++ 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's a temporary
        return this;
    }
}

تحرير: لاحظ أنني لم أحدد opAssign(ref UniquePtr!T that). هذا هو مشغل تعيين النسخ ، وإذا حاولت تحديده ، فسيخطئ المترجم لأنك أعلنت ، في @disable خط ، أنه ليس لديك شيء من هذا القبيل.

أعتقد أنه إذا كنت بحاجة إلى المصدر لفقدان المورد الذي قد تكون في ورطة. ومع ذلك ، يمكنك في كثير من الأحيان تجنب الحاجة إلى القلق بشأن العديد من المالكين ، لذلك قد لا تكون مشكلة في معظم الحالات.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top