2016-07-08 4 views
1

[이것은 can memcpy() be used to change “const” member data?의 후속 조치입니다. 그리고 Idiomatic Way to declare C++ Immutable Classes은 실제로 문제가 발생합니다. 특히 this 대답은 "불변의 데이터를 중심으로 한 언어에서, 그것은 (논리적 인) 불변성에도 불구하고 데이터를"이동할 "수 있다는 것을 알게 될 것입니다. ]"const"데이터를 바꾸기 위해 "new"배치를 사용할 수 있습니까?


structconst와 회원

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

다른 값을 갖는 새 point2d 인스턴스를 가리킬 수 있습니다 point2d에 대한 포인터를 보유하는 클래스 감안할. Bar

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; // (0, 0) 
    bar.move_x(3141); // (3141, 0) 

모두 point2dBar가 원하는대로 정확하게 일이다; 예, point2d은 완전히 변경할 수 없습니다.

그러나 실제로는 회원 데이터로 point2d 인스턴스를 저장하는 Bar의 다른 구현이 필요합니다. 이것을 달성 할 수있는 방법이 있습니까? 게재 위치 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도 OK! 아니면 둘 다, 유일한 해결책은 Bar입니까?

+0

왜 귀하가 링크 된 "증거"가 새로운 게재 위치와 관련이 없으면 배치 결과가 UB가되는 이유는 무엇입니까? 내가 뭘 놓치고 있니? –

+0

@Lightness 링크 된 답변의 주석은 배치가 'new'는'memcpy()'와 같습니다. –

+2

오, 주석. 나는 Sam와 동의 할 것이다. 새로운 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의 모든 포인터와 참조는 파기와 함께 무효화되며 재구성 후 유효한 상태로 돌아 가지 않으므로 파서를 업데이트 할 것입니다. 현재 표준에 액세스 할 수 없습니다. –