2016-09-30 15 views
1
#include <iostream> 
#include <cstring> 
// This struct is not guaranteed to occupy contiguous storage 
// in the sense of the C++ Object model (§1.8.5): 
struct separated { 
    int i; 
    separated(int a, int b){i=a; i2=b;} 
    ~separated(){i=i2=-1;} // nontrivial destructor --> not trivially copyable 
    private: int i2;  // different access control --> not standard layout 
}; 
int main() { 
    static_assert(not std::is_standard_layout<separated>::value,"sl"); 
    static_assert(not std::is_trivial<separated>::value,"tr"); 
    separated a[2]={{1,2},{3,4}}; 
    std::memset(&a[0],0,sizeof(a[0])); 
    std::cout<<a[1].i;  
    // No guarantee that the previous line outputs 3. 
} 
// compiled with Debian clang version 3.5.0-10, C++14-standard 
// (outputs 3) 
  1. Что такое обоснование стандартного ослабления гарантий того, что эта программа может показать неопределенное поведение?Массива несмежных объектов

  2. В стандарте указано: «Объект типа массива содержит смежно выделенный непустой набор из N подобъектов типа T». [dcl.array] §8.3.4. Если объекты типа T не занимают непрерывное хранилище, как может массив таких объектов?

редактировать: удалена, возможно, отвлекают пояснительный текст

+2

Что значит объект не занимает смежное пространство? Вы говорите о дополнении, которое может находиться между переменными-членами? – NathanOliver

+0

Математика говорит, что запрос «разреженных массивов» в Google может помочь вам ??? , извините, мой английский слишком плохой, чтобы помочь глубже. –

+1

Для вашего первого вопроса: Потому что никто не хочет проектировать C++ вокруг C-материала, такого как 'memset'. C-структурам необходимо работать с 'memset' для совместимости, остальное на самом деле не имеет значения. –

ответ

2

1. Это пример бритвы Оккама, принятый драконов, которые на самом деле писать компилятор: Не дают больше гарантий, чем необходимо, чтобы решить эту проблему, потому что в противном случае ваша рабочая нагрузка будет удвоена без компенсации. Сложные классы, адаптированные к фантастическому оборудованию или к историческому оборудованию, были частью проблемы. (Намекая на BaummitAugen и M.M)

2. (смежный = разделяя общую границу, рядом или вместе в последовательности)

Во-первых, это не то, что объекты типа Т всегда или никогда не занимают непрерывного хранения. В одном бинарнике могут быть разные макеты памяти для одного и того же типа.

[class.derived] §10 (8): Базовый класс подобъектов может иметь компоновку, отличную от ...

Это было бы достаточно, чтобы откинуться назад и быть уверен, что то, что происходит на наших компьютерах не противоречит стандарту. Но давайте исправим вопрос. Лучше задать вопрос:

Является ли стандартное разрешение массивами объектов, которые не занимают непрерывное хранение отдельно, в то же время каждый два последовательных подобъекта имеют общую границу?

Если это так, это сильно повлияет на то, как char * арифметика относится к арифметике T *.

В зависимости от того, понимаете ли вы стандартную цитату OP, означающую, что только субообъекты имеют общую границу или что также в каждом подобъекте байты имеют общую границу, вы можете прийти к разным выводам.

Предполагая, что первый, вы обнаружите, что «смежно выделено» или «хранятся смежно» может просто означать & а [п] == & а [0] + п (§23.3.2.1), которая является утверждение о subobject, что не означает, что массив находится в пределах одной последовательности смежных байтов.

Если предположить сильную версию, вы можете прийти к «элементу смещение == SizeOf (T)» вывод перенесенный в T* versus char* pointer arithmetic Это также означает, что один может заставить иначе возможно несмежные объекты в пределах макета объявив их T t [1]; вместо T t;

Теперь, как решить эту проблему?Существует принципиально неоднозначное определение оператора sizeof() в стандарте, который, по-видимому, является реликтоном того времени, когда, по крайней мере, на архитектуру, грубо сравните макет, что уже не так. (How does placement new know which layout to create?)

В применении к классу, результат [из SizeOf()] является количеством байт в объекте этого класса, включая любые дополнения, необходимые для размещения объектов данного типа в массиве. [expr.sizeof] §5.3.3 (2)

Но подождите, количество требуемого заполнения зависит от компоновки, а один тип может иметь более одного макета. Таким образом, мы обязаны добавить зерно соли и взять минимум на все возможные макеты или сделать что-то одинаково произвольное.

Наконец, определение массива выиграет от значения в терминах char * арифметики, если это предназначение. В противном случае ответ на вопрос 1 применяется соответственно.


Несколько замечаний, связанных с компанией удаляемых ответы и комментарии: Как обсуждается в Can technically objects occupy non-contiguous bytes of storage?, несмежные объекты на самом деле существуют. Кроме того, memseting подобъекта наивности может привести к аннулированию несвязанных подобъектов содержащего объекта, даже для абсолютно непрерывных, тривиального Copyable объектов:

#include <iostream> 
#include <cstring> 
struct A { 
    private: int a; 
    public: short i; 
}; 
struct B : A { 
    short i; 
}; 
int main() 
{ 
    static_assert(std::is_trivial<A>::value , "A not trivial."); 
    static_assert(not std::is_standard_layout<A>::value , "sl."); 
    static_assert(std::is_trivial<B>::value , "B not trivial."); 
    B object; 
    object.i=1; 
    std::cout<< object.B::i; 
    std::memset((void*)&(A&)object ,0,sizeof(A)); 
    std::cout<<object.B::i; 
} 
// outputs 10 with g++/clang++, c++11, Debian 8, amd64  

Таким образом, можно предположить, что MemSet в вопросе посте может нуль а [1]. i, так что программа будет выводить 0 вместо 3.

Существует несколько случаев, когда можно было бы использовать memset-подобные функции с объектами C++. (Обычно деструкторы подобъектов будут ошибочно выполняться, если вы это сделаете.) Но иногда хочется счистить содержимое «почти-POD'-класса» в своем деструкторе, и это может быть исключение.

+0

Поскольку можно разместить объект в подходящем совпадающем массиве символов соответствующего размера, кажется, что да, по крайней мере, «по крайней мере,« может привести к тому, что в противном случае могут быть несмежные объекты в непрерывный макет », независимо от интерпретации арифметики указателя. –

+0

Кроме того, поскольку можно вручную вызвать деструктор, а затем принудительно поместить новый объект в теперь пустое место хранения, кажется, что реализация не имеет другого выбора, кроме как использовать один и тот же непрерывный макет для всех наиболее производных объектов того же типа. –

+0

@ н.м. Я предполагаю, что вы имеете в виду размещение - новое, но «несмежный» макет остается, могут быть части объекта, которые не помещены в буфер. Важным примером этого является vtable. –