سؤال

لدي الكود التالي الذي يجمع ويعمل بشكل جيد:

template<typename T>
T GetGlobal(const char *name);

template<>
int GetGlobal<int>(const char *name);

template<>
double GetGlobal<double>(const char *name);

ومع ذلك أريد إزالة الوظيفة "الافتراضية".وهذا يعني أنني أريد إجراء جميع المكالمات إلى GetGlobal<t> حيث لا يمثل 't' خطأً صحيحًا أو خطأً مزدوجًا.

على سبيل المثال، يجب أن يكون GetGlobal<char>() خطأ في وقت الترجمة.

لقد حاولت فقط حذف الوظيفة الافتراضية، ولكن كما تخيلت، تلقيت الكثير من الأخطاء..فهل هناك طريقة "لتعطيلها" والسماح بالاتصال بالإصدارات المتخصصة من الوظيفة فقط؟

شكرًا!

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

المحلول

للحصول على خطأ في وقت الترجمة، قم بتطبيقه على النحو التالي:

template<typename T>
T GetGlobal(const char *name) { T::unimplemented_function; }
// `unimplemented_function` identifier should be undefined

إذا كنت تستخدم Boost، فيمكنك جعله أكثر أناقة:

template<typename T>
T GetGlobal(const char *name) { BOOST_STATIC_ASSERT(sizeof(T) == 0); }

يضمن C++ Standard عدم وجود مثل هذا النوع الذي يساوي حجمه 0، لذلك سوف تحصل على خطأ في وقت الترجمة.

مثل الهيئة الفرعية للتنفيذ اقترح في تعليقاته أن الأخير يمكن اختزاله إلى:

template<typename T>
T GetGlobal(const char *name) { char X[!sizeof(T)]; }

أفضل الحل الأول، لأنه يعطي رسالة خطأ أكثر وضوحًا (على الأقل في Visual C++) من الحلول الأخرى.

نصائح أخرى

على الرغم من أنه سؤال قديم وعفا عليه الزمن، إلا أنه قد يكون من المفيد الإشارة إلى ذلك C++11 تم حل هذه المشكلة باستخدام الوظائف المحذوفة:

template<typename T>
T GetGlobal(const char *name) = delete;

template<>
int GetGlobal<int>(const char *name);

تحديث

لن يتم تجميع هذا تحت MacOS llvm 8.ويرجع ذلك إلى عيب لا يزال معلقًا منذ 4 سنوات (انظر تقرير الشوائب هذا).

سوف يناسب الحل البديل التالي المشكلة (باستخدام ملف static_assert بناء).

template<typename T>
T GetGlobal(const char *name) {
    static_assert(sizeof(T) == 0, "Only specializations of GetGlobal can be used");
}

template<>
int GetGlobal<int>(const char *name);

تحديث

يحتوي Visual Studio 15.9 على نفس الخطأ.استخدم الحل البديل السابق لذلك.

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

template<typename T>
struct GlobalGetter;

template<>
struct GlobalGetter<int> {
  static int GetGlobal(const char *name);
};

template<>
struct GlobalGetter<double> {
  static double GetGlobal(const char *name);
};

template<typename T>
T GetGlobal(const char *name)
{
  return GlobalGetter<T>::GetGlobal(name);
}

وأود أن أقترح عدم تقديم فعلا التنفيذ، مجرد إعلان الادنى من هذه الطريقة.

والخيار الآخر سيكون لاستخدام ASSERT وقت الترجمة. دفعة لديها عدد من هذه الحيوانات.

namespace mpl = boost::mpl;
BOOST_MPL_ASSERT((mpl::or_< boost::same_type<T, double>,
                            boost::same_type<T, int> >));

وهناك أيضا في النسخة رسالة نظيره، الذي من شأنه أن يساعد.

فيما يلي تقنيات بديلة لاستخدام التعزيز:

قم بتعريف typedef إلى اسم تابع

يعمل هذا لأن البحث عن الاسم لـ DONT يحدث فقط عند استبدال 'T'.هذه نسخة مشابهة (لكن قانونية) للمثال الذي قدمه كيريل

template <typename T>
T GetGlobal (const char * name) {
    typedef typename T::DONT CALL_THIS_FUNCTION;
}

استخدم نوع إرجاع غير مكتمل

هذه التقنية لا تصلح للتخصصات، ولكنها تصلح للأحمال الزائدة.الفكرة هي أنه من القانوني الإعلان عن دالة تُرجع نوعًا غير مكتمل، ولكن لا يجوز استدعاؤها:

template <typename T>
class DONT_CALL_THIS_FUNCTION GetGlobal (const char * name);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top