تخصص القالب الجزئي للوظائف المجانية - أفضل الممارسات

StackOverflow https://stackoverflow.com/questions/2403928

سؤال

نظرا لأن معظم مبرمجي C ++ يجب أن يعرفون، فإن تخصص القالب الجزئي للوظائف المجانية غير مسموح بها. على سبيل المثال، ما يلي غير قانوني C ++:

template <class T, int N>
T mul(const T& x) { return x * N; }

template <class T>
T mul<T, 0>(const T& x) { return T(0); }

// error: function template partial specialization ‘mul<T, 0>’ is not allowed

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

template <class T, int N>
struct mul_impl
{
    static T fun(const T& x) { return x * N; }
};

template <class T>
struct mul_impl<T, 0>
{
    static T fun(const T& x) { return T(0); }
};

template <class T, int N>
T mul(const T& x)
{
    return mul_impl<T, N>::fun(x);
}

إنه أكثر ضخمة وأقل موجزة، لكنه يحصل على الوظيفة المنجزة - وعلى حد أقدم المستخدمين mul تشعر بالقلق، فهم يحصلون على التخصص الجزئي المرغوب فيه.


أسئلتي هي: عند كتابة الوظائف المجانية القاسية (التي تهدف إلى استخدامها من قبل الآخرين)، يجب عليك تفويض التنفيذ تلقائيا إلى وظيفة طريقة ثابتة للفئة، بحيث يمكن لمستخدمي مكتبتك تنفيذ التخصصات الجزئية في الإرادة، أو القيام به أنت فقط تكتب الدالة القيبذة الطريقة العادية، وتعيش مع حقيقة أن الناس لن يكونوا قادرين على تخصيصهم؟

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

المحلول

كما يقول Litb، ADL متفوق حيث يمكن أن تعمل، وهو أساسا كلما تم استنتاج معلمات القالب من معلمات المكالمات:

#include <iostream>

namespace arithmetic {
    template <class T, class S>
    T mul(const T& x, const S& y) { return x * y; }
}

namespace ns {
    class Identity {};

    // this is how we write a special mul
    template <class T>
    T mul(const T& x, const Identity&) {
        std::cout << "ADL works!\n";
        return x;
    }

    // this is just for illustration, so that the default mul compiles
    int operator*(int x, const Identity&) {
        std::cout << "No ADL!\n";
        return x;
    }
}

int main() {
    using arithmetic::mul;
    std::cout << mul(3, ns::Identity()) << "\n";
    std::cout << arithmetic::mul(5, ns::Identity());
}

انتاج:

ADL works!
3
No ADL!
5

التحميل الزائد + ADL يحقق ما كنت قد تحققته من قبل متخصص جزئيا قالب الوظيفة arithmetic::mul بالنسبة S = ns::Identity. وبعد لكنها تعتمد على المتصل للاتصال بها بطريقة تسمح ب ADL، وهذا هو السبب في أنك لا تتصل أبدا std::swap صراحة.

إذن السؤال هو، ماذا تتوقع من مستخدمي مكتبةك أن تضطر إلى التخصص جزئيا قوالب وظيفتك ل؟ إذا كانوا يخصصون لهم لأنواع (كما هو الحال عادة مع قوالب الخوارزمية)، فاستخدم ADL. إذا كانوا يذهبون إلى متخصصين في معلمات القالب عدد صحيح، كما هو الحال في مثالك، فما أعتقد أن عليك أن تفويض إلى فئة. لكنني لا أتوقع عادة طرفا ثالثا تحديد ما يجب أن يفعله الضرب بنسبة 3 - ستفعل مكتبتي الكل الأعداد الصحيحة. يمكن أن أتوقع بشكل معقول طرف ثالث تحديد ما الضرب الذي بمنه octonion. سوف تفعل.

تعال للتفكير في الأمر، ربما كان للأسف مثالا أفضل بالنسبة لي للاستخدام، لأنني arithmetic::mul يشبه مربق operator*, ، لذلك ليس هناك حاجة حقيقية إلى التخصص mul في مثالي. ثم سأتخصص / adl-overload للحصول على المعلمة الأولى، لأن "الهوية إلى قوة أي شيء هي الهوية". نأمل أن تحصل على الفكرة، رغم ذلك.

أعتقد أن هناك الجانب السلبي ل ADL - أنها تسطح مسطحات الأسماء بشكل فعال. إذا كنت أرغب في استخدام ADL إلى "تنفيذ" كلاهما arithmetic::sub و sandwich::sub لفصل بلدي، ثم يمكن أن أكون في ورطة. لا أعرف ماذا يجب أن يقول الخبراء عن ذلك.

التي أقصد بها:

namespace arithmetic {
    // subtraction, returns the difference of lhs and rhs
    template<typename T>
    const T sub(const T&lhs, const T&rhs) { return lhs - rhs; }
}

namespace sandwich {
    // sandwich factory, returns a baguette containing lhs and rhs
    template<typename SandwichFilling>
    const Baguette sub(const SandwichFilling&lhs, const SandwichFilling&rhs) { 
      // does something or other 
    }
}

الآن، لدي نوع ns::HeapOfHam. وبعد أرغب في الاستفادة من ADL STD :: Swap

namespace ns {
    HeapOfHam sub(const HeapOfHam &lhs, const HeapOfHam &rhs) {
        assert(lhs.size >= rhs.size && "No such thing as negative ham!");
        return HeapOfHam(lhs.size - rhs.size);
    }
}

أرغب أيضا في الاستفادة من ADL من STD :: Swap

namespace ns {
    const sandwich::Baguette sub(const HeapOfHam &lhs, const HeapOfHam &rhs) {
        // create a baguette, and put *two* heaps of ham in it, more efficiently
        // than the default implementation could because of some special
        // property of heaps of ham.
    }
}

إنتظر دقيقة. لا أستطيع أن أفعل ذلك، هل يمكنني؟ وظيفتان مختلفتان في مساحات الأسماء المختلفة مع نفس المعلمات وأنواع العودة المختلفة: ليس عادة مشكلة، وهذا ما أسماء الأسماء إليه. لكنني لا أستطيع adl- ify لهم كلاهما. ربما أنا أفتقد شيئا واضحا حقا.

راجع للشغل، في هذه الحالة يمكن أن أتخصص تماما كل من arithmetic::sub و sandwich::sub. وبعد المتصلين سوف using واحد أو آخر، والحصول على الوظيفة المناسبة. يتحدث السؤال الأصلي عن التخصص الجزئي، لذلك يمكننا أن ندعي أن التخصص ليس خيارا، دون أن أجعلني فعلا من صنع كيبوفهام قالب فئة؟

نصائح أخرى

إذا كنت تكتب مكتبة لاستخدامها في مكان آخر أو من قبل أشخاص آخرين يقومون بالشيء من الهيكل / الفصل. إنه أكثر من التعليمات البرمجية ولكن مستخدمي مكتبتك (ربما مستقبلك!) ستشكرك. إذا كان هذا هو رمز الاستخدام واحد، فلن يؤذيك فقدان التخصص الجزئي.

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