#c #templates #function #callback #pass-by-reference
#c #шаблоны #функция #Обратный вызов #передача по ссылке
Вопрос:
Я шаблонизирую класс queue, чтобы я мог использовать его с чем угодно, от целых чисел до любых структур, которые мне нужно определить.
Мне нужно передать функцию сравнения конструктору класса, предопределенную функцию сравнения для целых чисел и тому подобное, а затем предоставить клиенту предоставлять любые функции сравнения, которые они могут захотеть. Но как мне это сделать?
template<typename Type>
int cmpFn(Type one, Type two)
{
if (one < two) return -1;
if (one > two) return 1;
return 0;
}
template <typename Type>
class Queue
{
public:
Queue()
{
Type *list = new Type[size];
// What do I do now?
// How to define this constructor?
// It must pass a comparison function
// to a private sort method in this class.
}
private:
void sortFunc(Type list, int(fn)(Type one, Type two)=cmpFn);
};
Вероятно, в приведенном выше коде есть какие-то ошибки, поскольку я просто записал его с самого начала, чтобы сделать свой вопрос более понятным. Но все, что меня интересует, это как передать функцию сравнения методу сортировки при определении класса.
Это личное упражнение, я не записан ни на какой курс и у меня нет доступа к каким-либо преподавателям. Я уже некоторое время гуглю этот вопрос, но не смог найти правильный ответ… Думаю, я задал не тот вопрос мистеру Google.
P.S. Клиент может захотеть предоставить функции сравнения для любого вида данных, например:
struct individual
{
string name;
int age;
double height;
};
Я предполагаю, что конструктор должен быть таким:
Queue(int (*fn)(Type, Type) = cmpFn);
Но как мне определить / реализовать это? Эту функцию обратного вызова будет использовать не сам объект очереди, а его метод: sort();
Комментарии:
1. Это не ответ, но в духе C я бы спроектировал это по-другому; а именно, ожидайте, что
T
в комплекте будетoperator<
или специализацияstd::less<T>
, и используйте это для сортировки. Возможно, даже предоставить итераторы, хотя это может не помочь для структуры, подобной очереди.
Ответ №1:
Вот рабочий, компилируемый пример того, что, я думаю, вам нужно:
#include <cstddef>
#include <string>
#include <iostream>
template<typename Type>
int cmpFn(Type one, Type two)
{
if (one < two) return -1;
if (one > two) return 1;
return 0;
}
template <typename Type>
class Queue
{
public:
// This is the typedef for your callback type
typedef int (*callback)(Type one, Type two);
Queue(Type *list, size_t size, callback func = cmpFn)
{
sortFunc(list, size, func); // works too
}
private:
void sortFunc(Type *list, size_t size, callback func) {
for (size_t i=0; i<size; i ) {
for (size_t j=0; j<size; j ) {
if (i == j) continue;
int val = (*func)(list[i], list[j]);
switch (val) {
case 1:
std::cout << list[i] << " is greater than " << list[j] << "n";
break;
case -1:
std::cout << list[j] << " is greater than " << list[i] << "n";
break;
case 0:
std::cout << list[i] << " and " << list[j] << " are equaln";
break;
}
}
}
}
};
int stringCmp(std::string one, std::string two) {
if (one.size() < two.size()) return -1;
if (one.size() > two.size()) return 1;
return 0;
}
int main() {
// compare ints, use generic comparison function (cmpFn)
int myInts[2] = {1, 2};
Queue<int> qi(myInts, 2);
// compare strings, use our own comparison function (stringCmp)
std::string myStrings[2] = {"foo", "bar"};
Queue<std::string> qs(myStrings, 2, stringCmp);
return 0;
}
Компиляция и выполнение приведенной выше программы должны дать вам этот результат:
2 is greater than 1
2 is greater than 1
foo and bar are equal
bar and foo are equal
В основном, что это делает:
Queue
Конструктор принимаетlist
массив, его размер и функцию обратного вызова.- Если функция обратного вызова не предусмотрена, используется универсальная функция (
cmpFn
). - Затем он вызывает
sortFunc
который перебирает все элементы вlist
массиве и сравнивает их с помощью функции обратного вызова.
В приведенном выше коде приведен пример с int
и std::string
.
Комментарии:
1. Спасибо! Это почти то, что мне нужно. Теперь мне нужно только выяснить, как сохранить ссылку на функцию обратного вызова в объекте класса. И наоборот, я не хочу, чтобы sortFunc автоматически вызывался конструктором, вместо этого клиент будет вызывать его при необходимости способом myqueue.sortFunc(); или myqueue->sortFunc(); Клиент указывает, какую функцию сравнения для сортировки использовать при построении объекта: Queue<int> myqueue(cmpFuncStrings);
2. @user971191: Для этого вам придется сделать
sortFunc
общедоступным (это было закрыто, поэтому я предполагал, что вы этого не сделаете). Если вы хотите сохранить обратный вызов,callback
определяется как тип, поэтому просто объявите элемент этого типа и определите его в конструкторе.3. У меня все это было все время перед глазами … спасибо, что указали на это! 🙂
Ответ №2:
Может быть, вот так:
void sortFunc(Type list, int(*fn)(Type one, Type two) = cmpFn<Type>);
Вероятно, лучше всего передавать аргументы по const-ссылке, а не по копии. Кто знает, будут ли они вообще копируемыми?
Комментарии:
1. Спасибо за ответ, но как мне заставить этот класс хранить функцию сравнения, предоставленную клиентом? Клиент предоставит способ сравнения элементов, содержащихся в списке. Я сам определяю только стандартную функцию сравнения. Я действительно запутался в этом.
2. А, я понял. Вы могли бы сделать функцию сравнения параметром шаблона вашего класса, а затем добавить соответствующий аргумент конструктора. Взгляните на то, как
set
это реализовано (или, по крайней мере, ознакомьтесь с его открытым интерфейсом).3. @user971191: И если вы не хотите, чтобы это было частью аргументов шаблона, вам придется выполнить некоторое удаление типа.