GCC 3.42 и VC2008 по-разному относятся к std :: transform, взять ссылку или копию функтора? - PullRequest
1 голос
/ 07 июля 2011

Я гуглил эту тему, связанную с функтором, и кажется, что люди обычно говорят, что стандартная библиотека по умолчанию возьмет функтор в качестве копии.Было найдено много примеров, и это верно для std::for_each.Компиляторы GCC и VC2008 дали один и тот же результат: оба берут копию функтора.

Но для std::transform я обнаружил разницу в коде тестирования ниже:

#include <iostream>
#include <vector>

using namespace std;
#define MSG(msg) cout << msg << endl;

struct MyFunctor{
    MyFunctor()
    {
        MSG("MyFunctor constructor");
    }

    MyFunctor(const MyFunctor& myf)
    {
        MSG("MyFunctor copy constructor");
    }

    ~MyFunctor()
    {
        MSG("MyFunctor destructor");
    }
    int operator()(int i)
    {
        i = -(i+1);
        return i;
    }
};

int main() 
{
    vector<int> myvec;
    myvec.push_back(1);
    myvec.push_back(1);
    myvec.push_back(1);
    myvec.push_back(1);
    std::transform(myvec.begin(),myvec.end(),myvec.begin(),MyFunctor());
    system("pause");
}

/*
gcc result:
MyFunctor constructor
MyFunctor destructor

vc2008 result:
MyFunctor constructor
MyFunctor copy constructor
MyFunctor copy constructor
MyFunctor destructor
MyFunctor destructor
MyFunctor destructor
*/

Результаты, по-видимому, говорят о том, что GCC использует MyFunctor в качестве ссылки, а vc2008 - как копию (так же, как они оба обрабатывают for_each).

Почему GCC и vc2008 ведут себя по-разному в этом вопросе, и какая из них должна быть правильной практикой?

1 Ответ

3 голосов
/ 07 июля 2011

Результат, по-видимому, говорит о том, что GCC использует MyFunctor в качестве ссылки, а vc2008 - как копию

Вы не можете вывести это из ваших результатов. У нас есть такие вещи, как «скопировать решения». Стандарт C ++ явно позволяет компилятору оптимизировать ненужные копии в определенных случаях. Вполне возможно, что преобразование libstdc ++ также принимает функтор по значению (я не проверял), и что компилятор G ++ просто избавляется от ненужных копий. Когда компилятор выполняет удаление копии, он объединяет два или более объектов в один. В вашем случае вы создали временный объект MyFunctor. Затем есть еще один объект MyFunction (параметр функции). GCC разрешено делать этот временный объект тем же, на который ссылается указанный параметр функции. Фактически, всякий раз, когда новый объект инициализируется временным объектом того же типа, компилятору разрешается исключить эту копию и объединить оба объекта в один.

Проверьте это: замените временный объект MyFunctor на имя переменной, которое вы объявили ранее:

MyFunctor mf;
...
std::transform(...,mf);

В этом случае исключение копирования не разрешено, поскольку mf - это не значение, а значение. Вы можете ожидать, что ваш конструктор копирования что-то напечатает.

...