std::pair move не пропущен по определению?

Я заметил кое-что очень странное в 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.)


Полный исходный код для тестирования (см. также ссылку на 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;

person Martin Ba    schedule 09.10.2013    source источник
comment
Я подумал, что конструктор pair может быть чем-то вроде pair( T&& = type1(), U&& = type2() ), т. е. он может создавать временные объекты при создании одной пары. Но это не так — есть явный тривиальный конструктор. Следующий вопрос: вы делаете это в режиме отладки?   -  person Yakk - Adam Nevraumont    schedule 10.10.2013
comment
@Yakk - см. мою заметку. отладка против выпуска не имеет значения   -  person Martin Ba    schedule 10.10.2013
comment
Ба, пропустил это. Имеет ли значение auto?   -  person Yakk - Adam Nevraumont    schedule 10.10.2013
comment
@Yakk - кажется, я видел, что auto не имеет значения. Мне придется перепроверить, как только я вернусь к машине с VC11.   -  person Martin Ba    schedule 10.10.2013
comment
Хммм... Это я тестил с VS2005 (без ходов и без авто) и там копия тоже всегда обтекается.   -  person Martin Ba    schedule 10.10.2013
comment
как сумасшедшая теория, может быть, move ctor путает оптимизатор vc11 elision? Должен быть проверяемым.   -  person Yakk - Adam Nevraumont    schedule 10.10.2013
comment
@Yakk, посмотри мой ответ. У вас есть доступ к VS2013, чтобы проверить, присутствует ли он в самой новой версии?   -  person Martin Ba    schedule 10.10.2013


Ответы (1)


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

Тестирование только с объектом, исключая 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, и он по-прежнему предотвратит перемещение).

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

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

У типов с аргументами по умолчанию в их копировании/перемещении не пропущено их инициализирующее копирование/перемещение.

Я добавил ошибка в MS.connect для этой проблемы.

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

person Martin Ba    schedule 10.10.2013
comment
Аккуратный! Итак, две проблемы: во-первых, они должны использовать хакерский дефолтный nullptr с надуманным типом для включения SFINAE, а во-вторых, потому что оптимизация elision блокируется дополнительными дефолтными параметрами (я думаю, неправильно). В C++11 (и в MSVC2013) они могут использовать параметры шаблона по умолчанию в ctor, чтобы сделать это без аргумента по умолчанию для конструктора. Я не знаю, есть ли это улучшение в стандартной библиотеке C++ MSVC2013, но в MSVC 2013 есть поддержка компилятора blogs.msdn.com/b/vcblog/archive/2013/06/ 28/ - person Yakk - Adam Nevraumont; 11.10.2013