2015-03-28 6 views
1

ideone linkПодмена экземпляров `std :: aligned_storage`, содержащих типы, не подлежащие тривиальному копированию, - неопределенное поведение?

#include <iostream> 
#include <type_traits> 
using namespace std; 

// Non-trivially-copyable type. 
struct NTC 
{ 
    int x;  
    NTC(int mX) : x(mX) { }  
    ~NTC() { cout << "boop." << x << endl; } 
}; 

int main() 
{ 
    using AS = aligned_storage_t<sizeof(NTC), alignof(NTC)>; 

    // Create two `std::aligned_storage` instances 
    // and "fill" them with two "placement-new-constructed" 
    // `NTC` instances. 
    AS as1, as2;   
    new (&as1) NTC{2}; 
    new (&as2) NTC{5}; 

    // Swap the `aligned_storages`, not their contents. 
    std::swap(as1, as2); 

    // Explicitly call `~NTC()` on the contents of the 
    // aligned storage instances. 
    NTC& in1{*static_cast<NTC*>(static_cast<void*>(&as1))}; 
    NTC& in2{*static_cast<NTC*>(static_cast<void*>(&as2))};  
    in1.~NTC(); 
    in2.~NTC(); 

    return 0; 
} 

ли выше код неопределенное поведение?

Вот что я думаю, что это происходит:

  • NTC является нетривиальным-копируемым типом.
  • Я создаю две ячейки памяти, подходящие для хранения NTC объектов (std::aligned_storage).
  • Я строю два экземпляра NTC непосредственно в ячейки памяти.
  • std::aligned_storage instances are PODTypes.

    Это означает, что тип совместим с типами, используемыми на языке программирования C, можно управлять с помощью функций библиотеки C: его можно создать с помощью std :: malloc, его можно скопировать с помощью std :: memmove и т. Д. , и могут быть обменены с библиотеками C напрямую, в двоичной форме.

  • Поскольку выровненные экземпляры хранилища являются типами POD, мне должно быть разрешено перемещать/свопировать/копировать их.
  • Переустановка выровненных экземпляров хранилища означает, что берет все байты из выровненного хранилища A и свопит их со всеми байтами из выровненного хранилища B.
  • Выполнение этого не будет вызывать деструктор/копировать-конструктор объектов, хранящихся внутри, NTC. KG

Мои достоинства: Не верно? Если не определено поведение, в какой части программы это происходит? И почему?


Новый потенциально правильно/неправильно информация (gathered from a deleted answer):

  • std::aligned_storage довольно много просто ЬурейеЕ для массива C-стиле.
  • std::swap has an overload for C-style arrays since C++11.
  • Эта перегрузка calls std::swap_ranges, которая свопирует каждый элемент в массив.
  • Следовательно, замена двух экземпляров std::aligned_storage должна менять их содержимое по элементам.

Я делаю какую-либо ошибку в этих новых предположениях?

+1

Первоначально я неправильно читал, и я мог исправить это сразу, но hvd получил его. –

ответ

5

Непосредственный доступ к байтам буфера после того, как в него был помещен нетривиально-скопируемый тип, является очень плохой идеей, но пока еще не определена.

Попытка доступа буфера после замены, как NTC нарушает правила наложения спектров, [basic.lval] P10:

Если программа пытается получить доступ к сохраненному значению объекта через glvalue, отличные от один из следующих типов поведение не определено:

(10.1) - динамический тип объекта,

[....]

Для сохранения динамического типа подразумевается копирование тривиально скопируемого типа через memcpy или эквивалент. Такие действия не допускаются для типов, не подлежащих тривиальному копированию, поэтому после их замены у вас больше нет объектов NTC.

+0

Спасибо за ответ. Все еще есть некоторые сомнения - что такое ** динамический тип **? Является ли это релевантным ** только с полиморфными объектами ** (которые, очевидно, также являются * не тривиально-скопируемыми *)? Если это так, принудительно использовало бы типы 'T', хранящиеся в экземплярах' std :: aligned_storage', чтобы не быть полиморфными (может быть, через 'static_assert'), чтобы обеспечить безопасную замену выровненных хранилищ? –

+0

@VittorioRomeo Для lvalue «динамический тип» - это тип объекта, к которому относится lvalue. Он определен в 1.3.7, и это не только для полиморфных объектов: задано 'struct A {}; struct B: A {} b; A &a = b; ', динамический тип' a' - 'B', но никакая языковая конструкция не будет использовать этот динамический тип. – hvd

+2

@VittorioRomeo. Для примера, в котором можно было бы поработать с нетривиально-скопируемым типом, рассмотрим идиому pimpl, но рассмотрим случай, когда по какой-либо причине для типа реализации требуется обратный указатель. Это не может работать, если вы меняете буфер, как и вы, даже если у этого типа нет виртуальных функций. – hvd