كيف يمكنك إنشاء وظيفة عضو في قالب ثابت تقوم بإجراءات على فئة القالب؟

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

سؤال

أحاول إنشاء وظيفة عامة تزيل التكرارات من ناقل STD ::. نظرًا لأنني لا أرغب في إنشاء وظيفة لكل نوع متجه ، أريد أن أجعل هذه وظيفة قالب يمكن أن تقبل المتجهات من أي نوع. هذا ما لدي:

//foo.h

Class Foo {

template<typename T>
static void RemoveVectorDuplicates(std::vector<T>& vectorToUpdate);

};

//foo.cpp

template<typename T>
void Foo::RemoveVectorDuplicates(std::vector<T>& vectorToUpdate) {
for(typename T::iterator sourceIter = vectorToUpdate.begin(); (sourceIter != vectorToUpdate.end() - 1); sourceIter++) {
        for(typename T::iterator compareIter = (vectorToUpdate.begin() + 1); compareIter != vectorToUpdate.end(); compareIter++) {
            if(sourceIter == compareIter) {
                vectorToUpdate.erase(compareIter);
            }
        }
    }
}

//SomeOtherClass.cpp

#include "foo.h"

...

void SomeOtherClass::SomeFunction(void) {
    std::vector<int> myVector;

    //fill vector with values

    Foo::RemoveVectorDuplicates(myVector);
}

ما زلت أحصل على خطأ في الارتباط ، لكنه يجمع بشكل جيد. أي أفكار حول ما أفعله خطأ؟

استكمال: بناءً على الإجابة التي قدمها Iraimbilanja ، ذهبت وأعيد كتابة الرمز. ومع ذلك ، فقط في حال أراد شخص ما أن يقوم رمز العمل بالقيام بوظيفة الإزالة ، ها هي:

//foo.h

Class Foo {

    template<typename T>
    static void RemoveVectorDuplicates(T& vectorToUpdate){
        for(typename T::iterator sourceIter = vectorToUpdate.begin(); sourceIter != vectorToUpdate.end(); sourceIter++) {
            for(typename T::iterator compareIter = (sourceIter + 1); compareIter != vectorToUpdate.end(); compareIter++) {
            if(*sourceIter == *compareIter) {
                compareIter = vectorToUpdate.erase(compareIter);
            }
        }
    }
};

تبين أنه إذا قمت بتحديد ناقل STD :: في التوقيع ، فإن التكرار لا يعملون بشكل صحيح. لذلك اضطررت للذهاب مع نهج أكثر عاما. أيضًا ، عند محو Compareiter ، ينتج التكرار التالي للحلقة استثناء مؤشر. يعتني بتناقص ما بعد compareiter على محو هذه المشكلة. قمت أيضًا بإصلاح الأخطاء في المقارنة بين التكرار وفي تهيئة compareiter في الحلقة الثانية.

تحديث 2:

لقد رأيت أن هذا السؤال حصل على تصويت آخر ، لذا اعتقدت أنني سأقوم بتحديثه باستخدام خوارزمية أفضل تستخدم بعض الخير C ++ 14. عملت بلدي السابق فقط إذا كان النوع المخزن في المتجه ينفذ المشغل == يتطلب مجموعة من النسخ والمقارنات غير الضرورية. وبعد فوات الأوان ، ليست هناك حاجة لجعلها عضوًا في الفصل. تتيح هذه الخوارزمية الجديدة مقارنًا مخصصًا ، وتقلص مساحة المقارنة مع العثور على التكرارات ويجعل عددًا أقل بكثير من النسخ. تم تغيير الاسم إلى erase_duplicates لتتوافق بشكل أفضل مع خوارزمية STL تسمية اتفاقيات.

template<typename T>
static void erase_duplicates(T& containerToUpdate) 
{
    erase_duplicates(containerToUpdate, nullptr);
}

template<typename T>
static void erase_duplicates(T& containerToUpdate, 
  std::function<bool (typename T::value_type const&, typename T::value_type const&)> pred) 
{
    auto lastNonDuplicateIter = begin(containerToUpdate);
    auto firstDuplicateIter = end(containerToUpdate);
    while (lastNonDuplicateIter != firstDuplicateIter) {
        firstDuplicateIter = std::remove_if(lastNonDuplicateIter + 1, firstDuplicateIter, 
            [&lastNonDuplicateIter, &pred](auto const& compareItem){
            if (pred != nullptr) {
                return pred(*lastNonDuplicateIter, compareItem);
            }
            else {
                return *lastNonDuplicateIter == compareItem;
            }
        });
        ++lastNonDuplicateIter;
    }
    containerToUpdate.erase(firstDuplicateIter, end(containerToUpdate));
}
هل كانت مفيدة؟

المحلول

اجابة قصيرة

حدد الوظيفة في الرأس ، ويفضل داخل تعريف الفئة.

اجابة طويلة

يعني تحديد وظيفة القالب داخل .CPP أنها لن تحصل #includeD في أي وحدات ترجمة: ستكون متاحة فقط لوحدة الترجمة التي يتم تعريفها فيها.

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

هناك نوعان من الحلول لهذا الإزعاج

أولاً, ، يمكنك إزالة #include "foo.h" من .cpp وأضف واحدة أخرى ، في نهاية التابع رأس:

#include "foo.cpp"

يتيح لك ذلك تنظيم ملفاتك باستمرار ، ولكنه لا يوفر المزايا المعتادة لتجميع منفصل (التبعيات الأصغر ، وتجميعات أسرع وندرة).

ثانيا, ، يمكنك فقط تحديد وظيفة القالب في .cpp وتشكيلها بشكل صريح لجميع الأنواع التي سيتم استخدامها معها.

على سبيل المثال ، يمكن أن يذهب هذا في نهاية .CPP لجعل الوظيفة قابلة للاستخدام مع intس:

template void Foo::RemoveVectorDuplicates(std::vector<int>*);

ومع ذلك ، فإن هذا يفترض أنك تستخدم القوالب فقط لتوفير بعض الكتابة ، بدلاً من توفير جنة حقيقية.

نصائح أخرى

بديل واحد لديك هو أولاً std::sort() المتجه ، ثم استخدامه مسبقًا std::unique() وظيفة لإزالة التكرارات. يستغرق هذا النوع من الوقت (nlog n) ، وإزالة التكرارات بعد ذلك يستغرق وقتًا فقط (n) حيث تظهر جميع التكرارات في كتلة واحدة. خوارزمية المقارنة الحالية "All-VS-All" تستغرق وقت O (n^2).

لا يمكنك تنفيذ وظيفة قالب في ملف .cpp. يجب أن يكون التنفيذ الكامل مرئيًا في أي مكان يتم إنشاءه.

فقط حدد الوظيفة داخل تعريف الفئة في الرأس. هذه هي الطريقة المعتادة لتنفيذ وظائف القالب.

سأقترح استخدام نهج أكثر "عامًا" ، بدلاً من تمرير حاوية ، تلقي فقط اثنين من التكرار.

شيء من هذا القبيل remove_duplicates (أولاً ، آخر) ، وسيعيد جهاز التكرار ، حتى تتمكن من الاتصال مثل الإزالة: v.erase(remove_duplicates(v.begin(), v.end()), v.end()).

template <typename It>
It remove_duplicate(It first, It last)
{
  It current = first;
  while(current != last) {
    // Remove *current from [current+1,last)
    It next = current;
    ++next;
    last = std::remove(next, last, *current);
    current = next;
  }
  return last;
}

لا علاقة لمشكلتك (التي تم شرحها بالفعل) ، لماذا هذه وظيفة ثابتة بدلاً من الإقامة على مستوى العالم في مساحة الاسم؟ سيكون هذا إلى حد ما C ++-IER.

لا أعتقد أن هذا الرمز يجمع ....

vectortoupdate.erase حيث std :: vector * vectortoupDate .... هل يلاحظ أي شخص آخر وجود * حيث يجب أن يكون هناك A &؟ هذا الرمز بالتأكيد لا يجري تجميعه. إذا كنت ستستخدم مؤشرًا إلى المتجه ، فيجب عليك استخدامه "->" بدلاً من ". أعلم أن هذا في الواقع من الصعب إرضاءه بعض الشيء ، لكن يشير إلى أن المترجم لا يهتم حتى رمزك ...

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