Помогите понять работу функциональных объектов? - PullRequest
2 голосов
/ 31 января 2011

Я нашел этот код в Википедии

class compare_class {
  public:
  bool operator()(int A, int B) const {
    return A < B;
  }
};
...
// Declaration of C++ sorting function.
template <class ComparisonFunctor> 
void sort_ints(int* begin_items, int num_items, ComparisonFunctor c);
...
int main() {
    int items[] = {4, 3, 1, 2};
    compare_class functor;
    sort_ints(items, sizeof(items)/sizeof(items[0]), functor);
}

Сначала мне было интересно, как параметры A и B передаются в operator () (int A, int B), когдаФунктор упоминался в sort_ints без каких-либо скобок.

Затем я решил, что A и B передаются объекту функции внутри функции sort_ints.Но не должно ли объявление sort_ints иметь «ComparisonFunctor *** c» вместо «ComparisonFunctor c», так как оно получает адрес функции?

Внутри функции sort_intsбудет ли вызов функции к функтору что-то вроде этого?

functor(*begin_items, *(begin_items+1));

Ответы [ 4 ]

6 голосов
/ 31 января 2011

Чтобы понять, почему sort_ints принимает свой параметр по значению, вы должны помнить, что объект, передаваемый в него в качестве компаратора, не обязательно является указателем на функцию.Если, например, вы используете объект функции compare_class для сравнения, то то, что вы передаете в функцию, - это конкретный объект типа compare_class.Вы не передаете адрес compare_class::operator(), поскольку operator() является функцией-членом, а не свободной функцией.То есть следующий код:

compare_class myComparator;
myComparator(a, b);

преобразуется в

compare_class myComparator;
myComparator.operator() (a, b);

Следовательно, параметр должен принимать компаратор по значению, а не по указателю, поскольку ему нужен объект-получательвместо указателя на функцию-член или указателя на объект-получатель.

3 голосов
/ 31 января 2011

Как вы уже заметили, в использовании sort_ints нет ничего, что указывало бы на его требования к функтору сравнения:

compare_class functor;
sort_ints(items, sizeof(items)/sizeof(items[0]), functor);

В самом sort_ints тоже ничего нет:

template <class ComparisonFunctor>
void sort_ints(int* begin_items, int num_items, ComparisonFunctor c);

И в class_compare мы можем вывести только функциональные возможности, которые будут использоваться, наблюдая, что он не предлагает никаких других функциональных возможностей:

class compare_class
{
  public:
     bool operator()(int A, int B) const { return A < B; }
};

Компилятор действительно оставляет его, пока не попытается скомпилировать реализацию sort_ints, используятипы, с которыми он создается, прежде чем выяснить, все ли это вместе.У sort_ints должно быть именно то утверждение, которое вы упомянули:

c(*begin_items, *(begin_items+1));    // note: functor argument is "c"

Итак, ваше понимание верно по всем пунктам.НО, стоит отметить, что предложенная функция C ++ 0x под названием Concepts была предназначена для того, чтобы сделать требования sort_int более явными, не рассматривая его реализацию.К сожалению, C ++ 0x пришлось отказаться от этой функции, так как времени и опыта было недостаточно для обеспечения ее оптимальности.Надеемся, что в будущей версии C ++ будет реализована такая возможность.Вы можете найти множество обсуждений Концепций в сети, и они должны помочь вам лучше понять общую проблему.

Это важная проблема, потому что, когда у вас есть только кусочки головоломки - например, функция sort_intsВы хотите использовать, но не пример ComparisonFunctor, тогда вам нужно изучить реализацию sort_ints, чтобы узнать, как создать этот Functor (если только вам не повезло и нет хорошей текущей документации).Вы можете случайно сделать свой код слишком зависимым от существующей реализации, чтобы ваш код ломался или замедлялся недопустимо, когда реализация sort_int изменяется немного позже - даже если этого достаточно, чтобы не нарушать тестовые примеры или код других пользователей илиожидания.Также вероятно, что небольшая ошибка в предоставленном ComparisonFunctor приведет к очень запутанному и сбивающему с толку сообщению об ошибке компилятора где-то в кишках sort_int - это не очень хороший способ предоставить пользователю абстрактную службу.Будем надеяться, что Concepts сделает это за один день ...!

2 голосов
/ 31 января 2011

Обратите внимание, что compare_class - это класс.Таким образом, он может быть объявлен и передан в качестве параметра функции, как вы используете другие классы.

Во-вторых, compare_class реализует operator().Это позволяет вам делать следующее:

compare_class obj;
obj(1, 2);

Теперь второе выражение во фрагменте кода выше похоже на вызов функции!Таким образом, в некотором смысле любой объект класса, который реализует operator(), может использоваться как функция.

Это точка функциональных объектов.Кроме того, у него есть другие преимущества по сравнению с указателями на функции, которые вы найдете в той же ссылке, которую вы указали в своем сообщении.ожидается что-то вроде следующего:

for (int i = 1; i < num_items; i++)
    if (c(begin_items[i-1], begin_items[i]))
    {
        // begin_items[i-1] is less than begin_items[i], do stuff
    }
1 голос
/ 31 января 2011

Да, это правильно.Оператор вызова функции применяется к имени объекта, а не к методу объекта.Более того, вы вообще не передаете адрес функции, вы передаете (копируете) объект.В объектах функторов нет данных, только оператор ()

...