c++ - std::pair move 没有在定义中被省略?

标签 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;

最佳答案

看来不是移动构造函数,也不是导致问题的模板化移动构造函数,而是 enable_if<is_convertable<... 的存在。在模板化移动构造函数中:

仅用一个对象进行测试,抛出 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__ << ...;
    }
    
  • 失败!已调用移动构造函数!

    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并且它仍然防止移动省略)。

    这也扩展到仅具有默认参数的复制构造函数的类型。也不会被淘汰。快速cross-check with gcc显示那里似乎没有任何此类问题。

简答

在其复制/移动构造函数中具有默认参数的类型不会省略其初始化复制/移动。

我添加了一个 bug on MS.connect对于这个问题。

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

关于c++ - std::pair move 没有在定义中被省略?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19282925/

相关文章:

c++ - 在 CRS 稀疏矩阵中查找值?

钻石设计中最后派生的 C++ 基类函数调用

c++ - 使用 C++ 在 map 中插入 Pair 作为键

c++ - 带有第三个参数(即比较器函数)的重载 sort() 如何工作?

c++ - C++ binary_search函数排序数组出现问题(通俗名称搜索)

c++ - 使用快捷方式创建 QAction,无需插入菜单

c++ - 扩展 typedef struct(兼容 VC++11)

c++ - 一个类的成员可以被命名为与其类型(另一个类)相同的名称吗?

c++ - 与 lambda 成对 vector 的 lower_bound

c++ - 这里循环携带的依赖在哪里?