std :: pair移动没有在定义上省略?

时间:2013-10-09 21:17:18

标签: c++ std-pair visual-c++-2012 copy-elision

我注意到Visual Studio 2012中有一些非常奇怪的事情:定义一对像这样的对象:

    auto objp = pair<int, LogMe>();

在VC11中删除该对的复制/移动,此调用将打印:

LogMe::LogMe - def.ctor!
LogMe::LogMe - move.ctor!
LogMe::~LogMe - dtor!

即,将创建一个临时对 ,然后将其移动到objp变量中。 (将其声明为pair<...> obj;仅记录默认的ctor)

我已单独使用LogMe测试对象进行交叉检查:

cout << "# Construct Object via auto obj = ...\n";
auto obj = LogMe();

# Construct Object via auto obj = ...
LogMe::LogMe - def.ctor!

此处的任务将被删除。

这似乎是针对VC11的,因为testing it in IDEOne(使用gcc 4.8.1)表明外部移动总是在那里被省略。

这里发生了什么? 无法依赖被删除的初始化副本让我感到紧张。

注意:发布与调试版本的测试显示相同的结果。 (我原本期望的,因为复制省略与MSVC中的优化标志无关。)


要测试的完整源代码(另请参阅ideone link):

#include "stdafx.h"
#include <iostream>
#include <map>

using namespace std;

struct LogMe {
    std::string member;

    LogMe() {
        cout << __FUNCTION__ << " - def.ctor!" << endl;
    }
    ~LogMe() {
        cout << __FUNCTION__ << " - dtor!" << endl;
    }
    LogMe(LogMe const&) {
        cout << __FUNCTION__ << " - cpy.ctor!" << endl;
    }
    LogMe& operator=(LogMe const&) {
        cout << __FUNCTION__ << " - cpy.assign.op!" << endl;
        return *this;
    }
    LogMe(LogMe&&) {
        cout << __FUNCTION__ << " - move.ctor!" << endl;
    }
    LogMe& operator=(LogMe&&) {
        cout << __FUNCTION__ << " - move.assign.op!" << endl;
        return *this;
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    {
        cout << "# Construct Object via auto obj = ...\n";
        auto obj = LogMe();
        cout << "# Construct pair<int, object> via auto objp = ...\n";
        auto objp = pair<int, LogMe>();
        cout << "# Construct pair<int, object> via pair objp2; ...\n";
        pair<int, LogMe> p2;
    }
    return 0;

1 个答案:

答案 0 :(得分:1)

似乎不是移动ctor,也不是导致问题的模板移动ctor,而是enable_if<is_convertable<...在模板移动中的存在ctor:

仅使用对象进行测试,将autopair从测试中抛出:

  • 好的,复制/移动省略:

            cout << "# Construct Object: auto obj = LogMe();\n";
            LogMe obj = LogMe();
    
            LogMe(LogMe&&) {
                cout << __FUNCTION__ ...
            }
    

而且,通过这样的测试:

    cout << "# Construct Object: LogMeTempl obj = LogMeTempl();\n";
    LogMeTempl obj = LogMeTempl();
    cout << "# Construct Object: LogMeTempl obj2;\n";
    LogMeTempl obj2;
  • 好的,复制动作也被忽略了:

    template<class Other>
    LogMeTempl(Other&& rhs
    //      , typename enable_if<is_convertible<Other, LogMeTempl>::value, void>::type ** = 0
    ) {
        cout << __FUNCTION__ << ...;
    }
    
  • 失败!移动ctor!

    template<class Other>
    LogMeTempl(Other&& rhs
            , typename enable_if<is_convertible<Other, LogMeTempl>::value, void>::type ** = 0
    ) {
        cout << __FUNCTION__ << ...;
    }
    

    请注意,enable_if可以缩减为enable_if<true, void>::type** = 0 - 如果事实任何其他默认参数都会执行(例如, int defaulted_param_on_move_ctor = 0,它仍会阻止移动省音)。

    这也扩展到只有具有默认参数的copy-ctor的类型。它也不会被遗忘。快速cross-check with gcc显示那里似乎没有任何此类问题。

简答

在复制/移动中使用默认参数的类型将其初始化副本/移动省略。

我为此问题添加了bug on MS.connect

我还为(N)RVO to IDEone添加了一个测试用例。即使没有默认参数,* N * RVO似乎在gcc中比VC ++更好。