未调用 Move ctor

发布于 2024-09-29 17:08:28 字数 1178 浏览 2 评论 0原文

我(又)做错了什么吗?

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

Am I doing something wrong (again)?

#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;
}

Please see the commented line in the code

Edited [taken from FredOverflow answer]

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()); 
}  

I'm getting output:

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)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

三五鸿雁 2024-10-06 17:08:28
Map m3(createMap());//<<---or at least here, but nope...

您正在看到回报值优化的实际效果。在 C++ 中,编译器可以优化复制返回的对象,并让函数直接使用存储结果的调用者对象。也不需要调用移动构造函数。

让函数变得更复杂,这样编译器就无法使用优化,你会看到移动的实际情况。例如:

Map createMap()
{
    Map a, b;
    if (rand())
        return a;
    return b;
}
Map m3(createMap());//<<---or at least here, but nope...

You are seeing return value optimization in action. In C++, the compiler is allowed to optimize away copying returned objects, and let the function work directly with the caller's object where the result is stored. There isn't any need to invoke the move constructor either.

Make the function more complicated, so that the compiler cannot use the optimization, and you'll see moving in action. For example:

Map createMap()
{
    Map a, b;
    if (rand())
        return a;
    return b;
}
溺孤伤于心 2024-10-06 17:08:28

您正在声明一个函数,而不是一个对象:

T name (T(blah));

相当于:

T name(T blah);

可被识别为函数声明。您可以使用额外的括号:

Map m2 ((Map(m1)));

这称为最令人烦恼的解析

You're declaring a function, not an object:

T name (T(blah));

Is equivalent to:

T name(T blah);

Which is recognizable as a function declaration. You can use extra parens:

Map m2 ((Map(m1)));

This is called the most vexing parse.

长不大的小祸害 2024-10-06 17:08:28

我稍微修改了您的 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++ 有一个类似于 < code>-fno-elide-constructors 您可以使用它来更好地理解移动语义。

I slightly modified your main routine to understand the output better:

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());
}

And here is the output with g++ -fno-elide-constructors:

default
Map()

copy
Map(const Map& pattern)

move

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

As others already pointed out, Map m2(Map(m1)); is indeed a function declaration, so you get no output. The second move is not interpreted as a function declaration, because createMap is not a type name. There are two move constructors involved here. One moves the temporary object created by evaluating Map() into the temporary object created by evaluating createMap(), and the second move initializes m3 from the latter. This is exactly what one would expect.

If you fix the first move by writing Map m2((Map(m1))); the output becomes:

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

Again, no surprises. The copy constructor is invoked by evaluating Map(m1), and that temporary object is then moved into m2. If you compile without -fno-elide-constructors, the move operations disappear, because they are replaced by even more efficient optimizations like RVO or NRVO:

default
Map()

copy
Map(const Map& pattern)

move
Map(const Map& pattern)

move
Map()

I'm sure Visual C++ has a compiler option similar to -fno-elide-constructors that you can play with to understand move semantics better.

猥琐帝 2024-10-06 17:08:28

该程序显示了预期的输出。

#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 上的预期输出。

This program shows expected output.

#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;
}

Note the changes to createMap(). It doesn't employ a direct temporary but a named return value. This program shows the intended output on Visual Studio 2010.

国际总奸 2024-10-06 17:08:28
Map createMap()
{
    return Map();
}

我认为编译器会对上述内容进行 RVO (返回值优化),因此不会创建任何临时对象。

如果将其更改为以下内容,您应该会看到您的移动向量被调用。

Map createMap()
{
    Map m;
    m.DoSomething(); // this should make the compiler stop doing RVO
    return m;
}
  • 有些编译器执行 RVO,无论编译器设置如何(调试/发布模式) - 例如 bcc32。我有一种感觉 VC 也会一样。
Map createMap()
{
    return Map();
}

I would think that the compiler would've done an RVO (return value optimization) on the above, thus no temporaries would ever be created.

If you change it to the following, you should see your move ctor getting invoked.

Map createMap()
{
    Map m;
    m.DoSomething(); // this should make the compiler stop doing RVO
    return m;
}
  • Some compilers do RVO regardless of compiler settings (debug / release mode) - e.g. bcc32. I have a feeling VC would be the same.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文