Move ctor не вызывается

Я делаю что-то не так (снова)?

#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 ответов

Решение

Я немного изменил ваш 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 что вы можете играть, чтобы лучше понять семантику перемещения.

Map m3(createMap());//<<---or at least here, but nope...

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

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

Map createMap()
{
    Map a, b;
    if (rand())
        return a;
    return b;
}

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

T name (T(blah));

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

T name(T blah);

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

Map m2 ((Map(m1)));

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

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

#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.

Map createMap()
{
    return Map();
}

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

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

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