تخصص القالب الجزئي للوظائف المجانية - أفضل الممارسات
-
18-09-2019 - |
سؤال
نظرا لأن معظم مبرمجي 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
واحد أو آخر، والحصول على الوظيفة المناسبة. يتحدث السؤال الأصلي عن التخصص الجزئي، لذلك يمكننا أن ندعي أن التخصص ليس خيارا، دون أن أجعلني فعلا من صنع كيبوفهام قالب فئة؟
نصائح أخرى
إذا كنت تكتب مكتبة لاستخدامها في مكان آخر أو من قبل أشخاص آخرين يقومون بالشيء من الهيكل / الفصل. إنه أكثر من التعليمات البرمجية ولكن مستخدمي مكتبتك (ربما مستقبلك!) ستشكرك. إذا كان هذا هو رمز الاستخدام واحد، فلن يؤذيك فقدان التخصص الجزئي.