سؤال

بعد الإجابة هذا السؤال الذي كنت أحاول العثور عليه is_complete القالب في مكتبة Boost وأدركت أنه لا يوجد مثل هذا القالب في Boost.TypeTraits.لماذا لا يوجد مثل هذا القالب في مكتبة Boost؟كيف ينبغي أن تبدو؟

//! Check whether type complete
template<typename T>
struct is_complete
{   
  static const bool value = ( sizeof(T) > 0 );
};

...

// so I could use it in such a way
BOOST_STATIC_ASSERT( boost::is_complete<T>::value );

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


حسنًا، لا يمكن حل هذه المشكلة بشكل عام دون انتهاك قواعد قاعدة ODR, ولكن هناك منصة محددة حل الذي يعمل بالنسبة لي.

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

المحلول

يمكن استخدام الإجابة التي قدمها Alexey Malistov على MSVC مع تعديل بسيط:

namespace 
{
    template<class T, int discriminator>
    struct is_complete {  
      static T & getT();   
      static char (& pass(T))[2]; 
      static char pass(...);   
      static const bool value = sizeof(pass(getT()))==2;
    };
}
#define IS_COMPLETE(X) is_complete<X,__COUNTER__>::value

لسوء الحظ، ال عداد الماكرو المحدد مسبقًا ليس جزءًا من المعيار، لذلك لن يعمل على كل مترجم.

نصائح أخرى

template<class T>
struct is_complete {
    static T & getT();
    static char (& pass(T))[2];
    static char pass(...);

    static const bool value = sizeof(pass(getT()))==2;
};

قد يكون الوقت متأخرًا بعض الشيء، ولكن حتى الآن، لم يعمل أي حل C++ 11 لكل من الأنواع الكاملة والمجردة.

إذن، أنت هنا.

مع VS2015 (v140)، g++ >= 4.8.1، clang >= 3.4، هذا يعمل:

template <class T, class = void>
struct IsComplete : std::false_type
{};

template <class T>
struct IsComplete< T, decltype(void(sizeof(T))) > : std::true_type
{};

شكرًا لبات أولزي لوفسانبات: https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/

مع VS2013 (V120):

namespace Details
{

    template <class T>
    struct IsComplete
    {
        typedef char no;
        struct yes { char dummy[2]; };

        template <class U, class = decltype(sizeof(std::declval< U >())) >
        static yes check(U*);

        template <class U>
        static no check(...);

        static const bool value = sizeof(check< T >(nullptr)) == sizeof(yes);
    };

} // namespace Details


template <class T>
struct IsComplete : std::integral_constant< bool, Details::IsComplete< T >::value >
{};

هذا مستوحى من الإنترنت و هل يؤكد ثابت أن اسم نوع القالب T غير مكتمل؟

أخشى أنك لا تستطيع تنفيذ مثل هذا is_complete سمات النوع.فشل التنفيذ الذي قدمه @Alexey في التجميع على G++ 4.4.2 وG++ 4.5.0:

خطأ:تهيئة الوسيطة 1 لـ "حرف ثابت (& is_Complete::pass(T))[2] [مع T = Foo]"

على جهاز Mac الخاص بي، مع تقييم G++ 4.0.1 is_complete<Foo>::value أين struct Foo; غير مكتملة الغلة ل true وهو أسوأ من خطأ المترجم.

T يمكن أن تكون كاملة أو غير كاملة في نفس البرنامج، اعتمادًا على وحدة الترجمة ولكنها دائمًا من نفس النوع.ونتيجة لذلك، كما علق أعلاه، is_complete<T> هو دائما نفس النوع كذلك.

لذلك إذا كنت تحترم ODR ليس من الممكن أن يكون is_complete<T> التقييم بقيم مختلفة حسب مكان استخدامه؛وإلا فهذا يعني أن لديك تعريفات مختلفة لـ is_complete<T> الذي يحظره ODR.

يحرر:كإجابة مقبولة، قمت بنفسي بالتوصل إلى حل يستخدم __COUNTER__ ماكرو لإنشاء مثيل مختلف is_complete<T, int> اكتب في كل مرة IS_COMPLETE يتم استخدام الماكرو.ومع ذلك، مع gcc، لم أتمكن من جعل SFINAE يعمل في المقام الأول.

يتطلب حل هذه المشكلة إجراء الحساب في الوسيطة الافتراضية لقالب السمات، حيث أن محاولة تغيير تعريف القالب تنتهك قاعدة ODR (على الرغم من أن مجموعة من __COUNTER__ و namespace {} يمكن العمل حول ODR).

هذا مكتوب بلغة C++ 11 ولكن يمكن تعديله للعمل في وضع C++ 03 لمترجم متوافق مع C++ 11 حديث إلى حد ما.

template< typename t >
typename std::enable_if< sizeof (t), std::true_type >::type
is_complete_fn( t * );

std::false_type is_complete_fn( ... );

template< typename t, bool value = decltype( is_complete_fn( (t *) nullptr ) )::value >
struct is_complete : std::integral_constant< bool, value > {};

العرض التوضيحي عبر الإنترنت.

يتم تقييم الوسيطة الافتراضية حيث يتم تسمية القالب، بحيث يمكنه التبديل بين التعريفات المختلفة سياقيًا.ليست هناك حاجة لتخصص وتعريف مختلف في كل استخدام؛ما عليك سوى واحد ل true وواحد ل false.

القاعدة مذكورة في §8.3.6/9، والتي تنطبق بالتساوي على الوسائط الافتراضية للوظيفة ووسائط القالب الافتراضية:

يتم تقييم الوسائط الافتراضية في كل مرة يتم فيها استدعاء الوظيفة.

لكن احذر، فإن استخدام هذا داخل القالب يكاد يكون من المؤكد أنه ينتهك ODR.يجب ألا يقوم القالب الذي تم إنشاء مثيل له على نوع غير مكتمل بأي شيء مختلف عما إذا تم إنشاء مثيل له على نوع كامل.أنا شخصيا أريد هذا فقط ل static_assert.

وبالمناسبة، قد يكون هذا المبدأ مفيدًا أيضًا إذا كنت تريد السير في الاتجاه الآخر ينفذ وظيفة __COUNTER__ باستخدام القوالب والتحميل الزائد.

مجرد الإشارة للإشارة إلى أن الإجابة (التي لم تقدمها لي) لسؤال غير ذي صلة تعطي حلاً للمشكلة is_complete<T> نموذج.

الجواب هو هنا.أنا لا ألصقه أدناه حتى لا أحصل على الفضل فيه عن طريق الخطأ.

لا يمكنني العثور على أي شيء في المعيار يضمن أن sizeof على النوع غير المكتمل سينتج 0.ومع ذلك، فإنه يتطلب أنه إذا كانت T غير مكتملة في مرحلة ما، ولكنها مكتملة لاحقًا في وحدة الترجمة تلك، فإن جميع الإشارات إلى T تشير إلى نفس النوع - لذا كما قرأتها، حتى لو كانت T غير مكتملة حيث كان القالب الخاص بك عند استدعائه، سيكون مطلوبًا القول بأنه مكتمل إذا اكتمل T في مكان ما في وحدة الترجمة تلك.

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