В 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;
Похоже, что проблема связана не с 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 ++.
Других решений пока нет …