2016-07-08 3 views
1

[Это продолжение после can memcpy() be used to change “const” member data?. И Idiomatic Way to declare C++ Immutable Classes действительно попадает в проблему, особенно this ответ «На языке, разработанном вокруг неизменяемых данных, он знал бы, что он может« перемещать »ваши данные, несмотря на свою (логическую) неизменность». ]может быть использовано «новое» для изменения данных «const»?


дан struct с const членами

struct point2d { const int x; const int y; }; // can't change to remove "const" 

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

struct Bar 
{ 
    std::unique_ptr<point2d> pPt_{ new point2d{ 0, 0 } }; 
    const point2d& pt() const { 
     return *pPt_; 
    } 

    void move_x(int value) { 
     pPt_.reset(new point2d{ pt().x + value, pt().y }); 
    } 
}; 

Клиенты Bar см:

Bar bar; // (0, 0) 
    bar.move_x(3141); // (3141, 0) 

Оба point2d и Bar работают точно по желанию; да, point2d абсолютно неизменен.

Тем не менее, мне бы очень понравилась другая реализация Bar, которая хранит экземпляр point2d в качестве данных элемента. Есть ли способ достичь этого? Использование места размещения new предположительно приводит к undefined behavior (см. Комментарий).

#include <new> 
struct Baz 
{ 
    point2d pt{ 0, 0 }; 

    void move_x(int value) { 
     // ** is this undefined behavior ? ** 
     new (&pt) point2d { pt.x + value, pt.y }; 
    } 
}; 

ли не с использованием point2d непосредственно в качестве данных элемента работы, вокруг (потенциал?) Непредсказуемого поведения?

struct Blarf 
{ 
    unsigned char pt_[sizeof(point2d)]; 
    const point2d& pt() const { 
     return *reinterpret_cast<const point2d*>(pt_); 
    } 

    Blarf() { 
     new (&pt_) point2d{ 0, 0 }; 
    } 

    void move_x(int value) { 
     new (&pt_) point2d{ pt().x + value, pt().y }; 
    } 
}; 

Что именно? Просто Blarf? Или Baz также хорошо? Или нет, и единственным решением является Bar?

+0

Почему вы говорите, что размещение новых результатов в UB, когда «доказательство», с которым вы связаны, не имеет ничего общего с размещением нового? Что мне не хватает? –

+0

@ Светлый комментарий в связанном ответе говорит, что размещение 'new' совпадает с' memcpy() ' –

+2

О, комментарии, бла. Не уверен, что я согласен с Сэмом. Размещение нового as-if разрушает оригинал (который четко определен, если он тривиально разрушен, например, 'int') и заменяет его чем-то новым. Имейте в виду, что «объект» - это область памяти.Но у него также есть тип. Эти два варианта не подходят для размещения новых. Я думаю, что вопрос о том, будет ли это быть UB, будет довольно интересным. Сначала я задал этот вопрос. –

ответ

4

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

Использование объекта после его жизни закончилась, как представлен пример кода сделал в то время я написал этот ответ в

pt.~point2d(); 
new (&pt) point2d { pt.x + value, pt.y }; 

является Неопределенное поведение.

Если вы настаиваете на использовании класс точки с const полей, вы можете работать вокруг этого, как это:

void move_x(int const value) 
{ 
    auto const old_pt = pt; 
    pt.~point2d(); 
    ::new (&pt) point2d { old_pt.x + value, old_pt.y }; 
} 

Это может чувствовать себя ненужным усложнением и возможным микро-неэффективностью, а скорее, ненужное усложнение точечный класс.

+0

Ooops ... Явный вызов ~ point2d() был добавлен позже, это вызов, необходимый для правильного уничтожения существующего экземпляра? –

+0

@ Dan: Угадайте, ваш компилятор полностью оптимизирует это. Это все о формальном, говоря компилятору, что он не может делать слишком умные вещи, основываясь на предположениях, которые сохраняются только из-за формального UB в противном случае. –

+0

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

Смежные вопросы