Должен ли я использовать функции или функторы без сохранения состояния? - PullRequest
8 голосов
/ 02 апреля 2011

Эти 2 куска кода делают то же самое. И это будет использоваться в функции сортировки, как вы можете видеть. Что лучше? Я обычно пишу последний. Но я видел, как некоторые кодеры делают это как прежний.

struct val_lessthan : binary_function<pair<string,int>, pair<string, int>, bool>
{
    bool operator() (const pair<string,int>& x, const pair<string,int>& y) const
    {
        return x.second < y.second;
    }
} val_lt;

и

bool val_lt(const pair<string,int>& x, const pair<string,int>& y) 
{
    return x.second < y.second;
}

Будет использовать его как:

std::sort(wordvector.begin(), wordvector.end(), val_lt);

Ответы [ 6 ]

5 голосов
/ 02 апреля 2011

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

В приведенном выше конкретном примере два способа написания функции сравнения примерно эквивалентны.

4 голосов
/ 02 апреля 2011

Причина, по которой некоторые люди предпочитают первую версию, заключается в том, что функторы могут быть тривиально встроены.

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

С простой функцией то, что на самом деле std::sort видит, это просто указатель на функцию, и во время компиляции ничего не говорит о , на которую функция указывает. Так что это не может быть встроено, если компилятор не выполнит довольно обширный анализ потока, чтобы увидеть, откуда появился указатель в этом конкретном вызове . И он, безусловно, выполнит эту оптимизацию в небольшом примере, подобном вашему, но если указатель функтора / функции был передан в качестве аргумента функции, например, из другого места, или он был прочитан из промежуточной структуры данных перед передачей в std::sort, тогда компилятор может не иметь возможности встроить версию указателя функции, поэтому он будет работать медленнее.

1 голос
/ 02 апреля 2011

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

if (val_lt(a,b))
{
//...
}

до

if(val_lessthan()(a,b))
{
// ...
}

В противном случае при выборе формы функтора лучше вызывать с неназванным объектом функтора. То есть:

std::sort(wordvector.begin(), wordvector.end(), val_lesstthan());

вместо:

val_lesstthan named;
std::sort(wordvector.begin(), wordvector.end(), named);

Отмена именования параметров и возвращаемых значений позволяет компилятору выполнять оптимизацию. Это относится к глобальной концепции, известной как RVO (оптимизация возвращаемого значения). В этом случае он, вероятно, освободит ваш код от создания одной копии.

1 голос
/ 02 апреля 2011

Я бы предпочел первое, как правило, но обычно предпочитал бы использовать шаблон:

template <class T>
struct val_lessthan : binary_function<pair<pair<T, T>, bool> {
    bool operator()(T const &x, T const &y) const { 
       return x.second < y.second;
    }
};

Использование .second ограничивает степень универсальности, но вы все равно получаете немного (например, если память служит, boost::tuple обеспечивает .first и .second для кортежей двух элементов. Как правило, будучи шаблон дает немного лучшую уверенность в том, что компилятор сможет генерировать встроенный код, поэтому, если вы заботитесь об эффективности, он может немного помочь (или нет, но вряд ли когда-либо причинит какой-либо вред).

0 голосов
/ 02 апреля 2011

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

0 голосов
/ 02 апреля 2011

Оба будут одинаково быстры.Почти ничтожная разница.

Когда вы используете функтор, это означает, что функция operator() имеет три параметра в коде, сгенерированном компилятором, первый параметр является указателем на сам объект val_lt, а второй итретьи параметры - это параметры, которые вы упомянули в подписи.Как то так:

//the possible code generated by the compiler!
bool operator() (val_lessthan *_this, const pair<string,int>& x, const pair<string,int>& y) const
              //^^^^^^^^^^^^^^^^^^^ note this!
{
    return x.second < y.second;
}
...