движение std :: pair не определено?

В 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, так как тестирование в IDEOne (который использует gcc 4.8.1) показывает, что посторонний ход исключается всегда.

Что тут происходит? Неспособность полагаться на то, что копия инициализации удаляется, заставляет меня нервничать.

Примечание. Тесты выпуска и отладки показывают одинаковый результат. (Что я и ожидал, поскольку копирование выполняется независимо от флагов оптимизации в MSVC.)


Полный исходный код для тестирования (см. Также ссылка на идеон):

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

5

Решение

Похоже, что проблема связана не с ctor-движением, ни ctor-шаблоном, а с наличием enable_if<is_convertable<... в шаблонном ходу ctor:

Тестирование только с объектом, бросая auto а также pair вне испытания:

  • ОК, скопировать / переместить

            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 и все равно предотвратить переезд).

    Это также распространяется на тип с копией-ctor только с аргументом по умолчанию. Это также не будет исключено. Быстрый перекрестная проверка с gcc показывает, что там, похоже, такой проблемы нет.

Короткий ответ

Типы с аргументами по умолчанию в их копоре / перемещении ctor не иметь их инициализацию копирования / перемещения.

Я добавил ошибка в MS.connect для этого вопроса.

Я также добавил тест-кейс для (N) RVO в IDEone. Даже без аргумента по умолчанию * N * RVO работает лучше в gcc, чем в VC ++.

1

Другие решения

Других решений пока нет …