Intereting Posts
Учитывая, что p является указателем, «p> nullptr» корректно сформирован? Чтение электронных писем из учетной записи Gmail POP3 с помощью libCurl продление срока службы временных Почему экземпляры шаблонов не могут быть выведены в `std :: reference_wrapper`? C ++ MSAPI 5: SetNotifyCallbackFunction не работает Копирование classа C ++ с переменной-членом ссылочного типа Функция для анализа строки с помощью токенов Компиляция статического исполняемого файла с помощью CMake Удаление одной конструкции приводит к некорректному выполнению Отображение значений графика при наведении мыши. – Обнаружение точек рассеяния Создание видео с использованием новейшего API ffmpeg (2017) Могу ли я оптимизировать код, который имеет 3 для циклов и 4 ifs? чтение данных из файла .txt и сохранение в вектор Enum to string: возвращает значение целочисленного числа enum, если оно недействительно / не найдено. Мониторинг заряда аккумулятора с помощью API Win32

Как создать статическую функцию члена шаблона, которая выполняет действия в classе шаблона?

Я пытаюсь создать общую функцию, которая удаляет дубликаты из std :: vector. Поскольку я не хочу создавать функцию для каждого типа вектора, я хочу сделать эту функцию шаблона, которая может принимать векторы любого типа. Вот что я имею:

//foo.h Class Foo { template static void RemoveVectorDuplicates(std::vector& vectorToUpdate); }; //foo.cpp template void Foo::RemoveVectorDuplicates(std::vector& 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 myVector; //fill vector with values Foo::RemoveVectorDuplicates(myVector); } 

Я продолжаю получать ошибку компоновщика, но он компилируется нормально. Любые идеи относительно того, что я делаю неправильно?

ОБНОВЛЕНИЕ: Основываясь на ответе Ираимбиланья, я пошел и переписал код. Однако на всякий случай кому-то нужен рабочий код для функции RemoveDuplicates, вот он:

 //foo.h Class Foo { template 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 :: vector в сигнатуре, iteratorы работают неправильно. Поэтому мне пришлось пойти с более общим подходом. Кроме того, при стирании compareIter, следующая итерация цикла создает исключение указателя. Пост-декремент сравнения по стиранию заботится об этой проблеме. Я также исправил ошибки в сравнении iteratorа и инициализацию compareIter во втором цикле.

ОБНОВЛЕНИЕ 2:

Я видел, что этот вопрос получил еще один голос, поэтому я решил, что обновляю его с помощью лучшего алгоритма, который использует некоторые возможности C ++ 14. Мой предыдущий работал только в том случае, если тип, хранящийся в векторе, реализовал оператор ==, и потребовал кучу копий и ненужных сравнений. И, задним числом, нет необходимости делать его членом classа. Этот новый алгоритм позволяет использовать специальный предикат сравнения, сжимает пространство сравнения, поскольку дубликаты найдены и значительно уменьшают количество копий. Имя было изменено на erase_duplicates чтобы лучше соответствовать соглашениям об именах алгоритмов STL.

 template static void erase_duplicates(T& containerToUpdate) { erase_duplicates(containerToUpdate, nullptr); } template static void erase_duplicates(T& containerToUpdate, std::function 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)); } 

Короткий ответ

Определите функцию в заголовке, предпочтительно внутри определения classа.

Длительный ответ

Определение функции шаблона внутри .cpp означает, что он не получит #include d в любые единицы перевода: он будет доступен только для единицы перевода, в которой он определен.

Следовательно, RemoveVectorDuplicates должен быть определен в заголовке, так как это единственный способ, по RemoveVectorDuplicates компилятор может текстовым образом заменить аргументы шаблона, а затем создать экземпляр шаблона, создав полезный class.

Существует два способа устранения этих неудобств

Во-первых , вы можете удалить #include "foo.h" из .cpp и добавить еще один, в конце заголовка :

 #include "foo.cpp" 

Это позволяет вам упорядочивать ваши файлы последовательно, но не предоставляет обычных преимуществ отдельной компиляции (меньшие зависимости, быстрее и реже компилирует).

Во-вторых , вы можете просто определить функцию шаблона в .cpp и явно создать экземпляр для всех типов, с которыми он когда-либо будет использоваться.

Например, это может пойти в конце .cpp, чтобы сделать функцию полезной с int s:

 template void Foo::RemoveVectorDuplicates(std::vector*); 

Тем не менее, это предполагает, что вы используете только шаблоны для сохранения некоторой типизации, а не для обеспечения истинной общности.

Один из вариантов, который у вас есть, – это сначала выполнить std::sort() вектор, а затем использовать ранее существовавшую функцию std::unique() для удаления дубликатов. Сорт принимает время O (nlog n), а удаление дубликатов после этого занимает только время O (n), поскольку все дубликаты появляются в одном блоке. Ваш текущий алгоритм сравнения «все-все-все» принимает время O (n ^ 2).

Вы не можете реализовать функцию шаблона в файле .cpp. Полная реализация должна быть видна в любом месте, где она была создана.

Просто определите функцию внутри определения classа в заголовке. Это обычный способ реализации функций шаблона.

Я предлагаю использовать более «общий» подход, вместо того, чтобы передавать контейнер, просто получите два iteratorа.

Что-то вроде It remove_duplicates (сначала, It last), и возвратит iterator, поэтому вы можете вызвать как remove: v.erase(remove_duplicates(v.begin(), v.end()), v.end()) ,

 template  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 …. кто-нибудь еще замечает, что есть *, где должен быть &? этот код определенно не компилируется. если вы собираетесь использовать указатель на вектор, вы должны использовать ‘->’ вместо ‘.’ Я знаю, что это на самом деле немного придирчиво, но он указывает, что компилятор даже не заботится о вашем коде …