6

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!

ここで割り当ては省略されます。

IDEOne (gcc 4.8.1 を使用) でテストすると、無関係な移動が常に省略されていることが示されるため、これは VC11 に固有のようです。

何が起きてる? 初期化コピーが省略されていることに頼ることができないことは、私を緊張させます。

注: リリース バージョンとデバッグ バージョンのテストでは、同じ結果が表示されます。(MSVCの最適化フラグとは無関係にコピー省略が実行されるため、これは予想していたでしょう。)


テストする完全なソースコード ( ideone リンクも参照):

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

1 に答える 1

2

問題を引き起こしているのは move ctor でもテンプレート化された move ctor でもないようですがenable_if<is_convertable<...、テンプレート化された move ctor 内に次のものが存在します。

オブジェクトだけでテストし、スローautopairてテストから外します。

  • OK、コピー/移動を省略:

            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;
  • OK、コピー、移動も省略されました:

    template<class Other>
    LogMeTempl(Other&& rhs
    //      , typename enable_if<is_convertible<Other, LogMeTempl>::value, void>::type ** = 0
    ) {
        cout << __FUNCTION__ << ...;
    }
    
  • 失敗!Move 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、それでもmove elision が防止されます)。

    これは、デフォルトの引数を持つ copy-ctor のみを持つ型にも拡張されます。それも省略されません。gcc で簡単にクロスチェックすると、そのような問題はないようです。

簡潔な答え

copy/move ctor にデフォルトの引数を持つ型では、初期化の copy/move が省略されていません

この問題について、MS.connect にバグを追加しました。

(N)RVO のテスト ケースも IDEoneに追加しました。デフォルトの引数がなくても、*N*RVO は VC++ よりも gcc でうまく機能するようです。

于 2013-10-10T19:30:29.620 に答える