2016-11-05 2 views
0

Я пытаюсь передать массив функции и не допускать запись «std :: unique_ptr» каждый раз и сделать возможной встроенную конструкцию, я ввожу typedef (ItemList) для псевдонима массива.C++ 11 странное поведение инициализации списка на Visual Studio 2013

#include <iostream> 
#include <memory> 

class Base { 
public: 
    Base() 
    { 
     std::cout << "Base ctor" << std::endl; 
    }; 

    virtual ~Base() 
    { 
     std::cout << "Base dtor" << std::endl; 
    }; 
}; 

typedef std::unique_ptr<Base> ItemList[]; 

template<typename T> 
class Derived : public Base { 
    T val; 
public: 
    Derived(T i) 
    { 
     val = i; 
     std::cout << "Derived ctor" << val << std::endl; 
    }; 

    ~Derived() 
    { 
     std::cout << "Derived dtor" << val << std::endl; 
    }; 
}; 

void dummyFunc(ItemList) 
{ 

} 

void testFunc() 
{ 
    dummyFunc(ItemList{ 
     std::make_unique<Derived<int>>(2), 
     std::make_unique<Derived<float>>(3.0f) 
    }); 
} 

//Entry point 
int main() 
{ 
    testFunc(); 
    return 0; 
} 

Это работает в соответствии с сборкой и отпечатками Debug;

Base ctor 
Derived ctor2 
Base ctor 
Derived ctor2 
Derived dtor2 
Base dtor 
Derived dtor2 
Base dtor 

Пока все хорошо. Но когда я создаю это в режиме Release (со всеми родными компиляторами), я получаю;

Base ctor 
Derived ctor2 
Base ctor 
Derived ctor3 
Derived dtor2 
Base dtor 

Второй элемент массива не разрушается при выходе из жизненного цикла массива. Единственный способ заставить его работать, как я ожидаю, использует режим инициализации или отладки стиля C++ 03;

ItemList tmpList = { 
    std::make_unique<Derived<int>>(2), 
    std::make_unique<Derived<float>>(2.0f) 
}; 

dummyFunc(tmpList); 

Это приводит к предполагаемому поведению (все деструкторы называются).

Я еще не тестировал это с каким-либо другим компилятором, но это ожидаемое поведение? Что я делаю неправильно, или я чего-то не хватает?

Update:

Интересно dtors называются, как и ожидалось с базовыми экземплярами;

dummyFunc(ItemList{ 
    std::make_unique<Base>(), 
    std::make_unique<Base>() 
}); 

Выходы;

Base ctor 
Base ctor 
Base dtor 
Base dtor 

И только инициализация массива (без вызова функции) ведет себя так же, как при вызове функции.

ответ

0

Переключиться на Visual Studio 2015. Это, вероятно, ошибка реализации в VS2013 компиляторе.

0

Ваш код на самом деле не строит с г ++ 6.2, а ошибка достаточно четко объяснить, почему вы не получите ожидаемого поведения:

foo.cc:44:15: error: taking address of temporary array 
    dummyFunc(ItemList{ 
       ^~~~~~~~~ 
     std::make_unique<Derived<int>>(2), 
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     std::make_unique<Derived<float>>(3.0f) 
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    }); 

Простое исправление было бы использовать:

using ItemList = std::initializer_list<std::unique_ptr<Base>>;

вместо массива.

Обновление: поведение VS может быть правильным:

Именующее выражение или Rvalue типа «массив NT» или «массив неизвестного неизбежно Т» может быть преобразован в prvalue типа «указатель на T». Результат является указателем на первый элемент массива.

+0

Интересно ... Он компилируется как по умолчанию, так и по умолчанию. Не уверен относительно поведения релиза на Mac, но будет проверять. Havent попробовал ваше решение. С другой стороны, я не уверен, действительно ли он должен жаловаться на использование временного массива. Это моя проблема, предупреждение будет лучшим поведением. Следует проверить стандарт на это поведение. –

+0

@AliNaciErdem, принимающий адрес rvalue, является незаконным, cf стандартом: 'Результат оператора unary & является указателем на его операнд. Операнд должен быть lvalue или квалифицированным идентификатором. «О поведении VS в выпуске я обновил свой ответ. – AntiClimacus

+0

Это все еще не объясняет поведение. Если он преобразуется в указатель на первый элемент, то почему первый элемент разрушен? Я не понимаю. Im хорошо, если объект будет уничтожен до передачи в func, но он должен быть уничтожен, так как он выделен в стеке. –