Move ctor не вызывается - PullRequest
0 голосов
/ 30 октября 2010

Я что-то не так делаю (опять)?

#include <iostream>
using std::cout;

struct Map
{
    Map()
    {
        cout << "Map()\n";
    }
    Map(const Map& pattern)
    {
        cout << "Map(const Map& pattern)\n";
    }
    Map(Map&& tmp)
    {
        cout << "Map(Map&& tmp)\n";
    }
};

Map createMap()
{
    return Map();
}

int main(int argc, char* argv[])
{
    //dflt
    Map m;
    //cpy
    Map m1(m);
    //move
    Map m2(Map(m1));//<<I thought that I create here tmp unnamed obj.
    Map m3(createMap());//<<---or at least here, but nope...
    return 0;
}

Пожалуйста, смотрите закомментированную строку в коде

Отредактировано [взято из ответа FredOverflow]

int main() 
{ 
    std::cout << "default\n"; 
    Map m; 

    std::cout << "\ncopy\n"; 
    Map m1(m); 

    std::cout << "\nmove\n";
    Map m2((Map(m1))); 

    std::cout << "\nmove\n"; 
    Map m3(createMap()); 
}  

Я получаю вывод:

default
Map()

copy
Map(const Map& pattern)

move
Map(const Map& pattern)//Here why not move ctor aswell as copy?

move
Map()
Map()
Map(Map&& tmp)

Ответы [ 5 ]

4 голосов
/ 30 октября 2010
Map m3(createMap());//<<---or at least here, but nope...

Вы видите оптимизацию возвращаемого значения в действии. В C ++ компилятору разрешено оптимизировать копирование возвращаемых объектов и позволить функции работать непосредственно с объектом вызывающей стороны, где хранится результат. Нет необходимости также вызывать конструктор перемещения.

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

Map createMap()
{
    Map a, b;
    if (rand())
        return a;
    return b;
}
3 голосов
/ 30 октября 2010

Вы объявляете функцию, а не объект:

T name (T(blah));

Эквивалентно:

T name(T blah);

Что можно узнать как объявление функции. Вы можете использовать дополнительные парены:

Map m2 ((Map(m1)));

Это называется самый неприятный анализ .

1 голос
/ 30 октября 2010

Эта программа показывает ожидаемый результат.

#include <iostream>
using std::cout;

struct Map
{
    Map()
    {
        cout << "Map()\n";
    }
    Map(const Map& pattern)
    {
        cout << "Map(const Map&)\n";
    }
    Map(Map&& tmp)
    {
        cout << "Map(Map&&)\n";
    }
};

Map createMap()
{
    Map m;
    return m;
}

int main(int argc, char* argv[])
{
    //dflt
    Map m;
    //cpy
    Map m1(m);
    //move
    Map m2 = createMap();//<<I thought that I create here tmp unnamed obj.
    std::cin.get();
    return 0;
}

Обратите внимание на изменения в createMap ().Он не использует прямое временное, но именованное возвращаемое значение.Эта программа показывает предполагаемый вывод в Visual Studio 2010.

1 голос
/ 30 октября 2010

Я немного изменил вашу main процедуру, чтобы лучше понять вывод:

int main()
{
    std::cout << "default\n";
    Map m;

    std::cout << "\ncopy\n";
    Map m1(m);

    std::cout << "\nmove\n";
    Map m2(Map(m1));

    std::cout << "\nmove\n";
    Map m3(createMap());
}

А вот вывод с g++ -fno-elide-constructors:

default
Map()

copy
Map(const Map& pattern)

move

move
Map()
Map(Map&& tmp)
Map(Map&& tmp)

Как уже отмечали другие, Map m2(Map(m1)); действительно является объявлением функции, поэтому вы не получите никакого вывода. Второй ход - , а не , интерпретируемый как объявление функции, потому что createMap не является именем типа. Здесь задействованы два конструктора перемещения. Один перемещает временный объект, созданный путем оценки Map(), во временный объект, созданный путем оценки createMap(), а второй шаг инициализирует m3 из последнего. Это именно то, что и следовало ожидать.

Если вы исправите первый ход, написав Map m2((Map(m1)));, вы получите:

move
Map(const Map& pattern)
Map(Map&& tmp)

Опять без сюрпризов. Конструктор копирования вызывается путем оценки Map(m1), и этот временный объект затем перемещается в m2. Если вы компилируете без -fno-elide-constructors, операции перемещения исчезают, потому что они заменяются еще более эффективными оптимизациями, такими как RVO или NRVO:

default
Map()

copy
Map(const Map& pattern)

move
Map(const Map& pattern)

move
Map()

Я уверен, что в Visual C ++ есть опция компилятора, похожая на -fno-elide-constructors, с которой вы можете поиграть, чтобы лучше понять семантику перемещения.

0 голосов
/ 01 ноября 2010
Map createMap()
{
    return Map();
}

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

Если вы измените его на следующее, вы должны увидеть, как вызывается ваш ход ctor.

Map createMap()
{
    Map m;
    m.DoSomething(); // this should make the compiler stop doing RVO
    return m;
}
  • Некоторые компиляторы выполняют RVO независимо от настроек компилятора (режим отладки / выпуска) - например, bcc32. У меня такое ощущение, что ВК будет такой же.
...