2016-06-12 2 views
4

배열을 만들려고하지만 개체의 기본 초기화가없는 다음 코드가 있습니다. 새로운 일이 생길 것 같은데 완벽하게 전달하고 싶습니다만, 객체의 소멸자가 emplace 함수 내부에서 호출된다는 것을 알았습니다. 다음배치 새롭고 완벽한 전달

start emplace 
0 constructed 
0 move constructed 
0 destructed 
end emplace 
start emplace 
1 constructed 
1 move constructed 
1 destructed 
end emplace 
1 
start emplace 
2 constructed 
2 move constructed 
2 destructed 
end emplace 
0 destructed 
1 destructed 
2 destructed 

그것은 객체가 일단 설치하다 함수 내부 (분명 UB 임) 한때 구성된 회 소멸되는 것을 나타내고, 다음과 같이

#include <iostream> 
#include <memory> // std::uninitialized_copy, std::allocator... 
#include <utility> // std::move... 
#include <bitset> 


struct Int { 

    int i; 

    Int () : i (-1) { std::cout << "default constructed\n"; } 
    Int (const int i_) : i (i_) { std::cout << i << " constructed\n"; } 
    Int (Int && int_) : i (std::move (int_.i)) { std::cout << i << " move constructed\n"; } 
    Int (const Int & int_) : i (int_.i) { std::cout << i << " copy constructed\n"; } 
    ~Int () { std::cout << i << " destructed\n"; i = -1; } 
}; 


template <typename T, size_t S = 64> 
class NoInitArray { 

    std::bitset<S> m_used; 

    T *m_array = reinterpret_cast < T* > (::operator new (sizeof (T) * S)); 

public: 

    T const &operator [ ] (const size_t idx_) const { 

     return m_array [ idx_ ]; 
    } 

    NoInitArray () { } 

    ~NoInitArray () { 

     for (size_t idx = 0; idx < S; ++idx) { 

      if (m_used [ idx ]) { 

       reinterpret_cast< const T* > (m_array + idx)->~T (); 
      } 
     } 
    } 

    template<typename ...Args> 
    void emplace (const size_t idx_, Args &&... value_) { 

     std::cout << "start emplace\n"; 

     m_used [ idx_ ] = 1; 

     new (m_array + idx_) T (std::forward<T> (value_) ...); 

     std::cout << "end emplace\n"; 
    } 
}; 


int main () { 

    NoInitArray<Int> nia; 

    nia.emplace (0, 0); 
    nia.emplace (1, 1); 

    std::cout << nia [ 1 ].i << std::endl; 

    nia.emplace (2, 2); 

    return 0; 
} 

이 프로그램을 실행 한 결과는 한 번 NoInitArray의 파괴에.

질문은 "왜 내 Int 객체의 소멸자가 emplace 함수 내부에서 호출 되었습니까?"라는 질문이 있습니다.

컴파일러, 최신 Clang/LLVM on Windhoze.

EDIT1 : Int 구조체에 이동 및 복사 생성자를 추가했습니다. 이제 계산이 일치합니다 (예 : 2 개의 구성 및 2 개의 삭제).

EDIT2 : 배치 변경 새 라인을 new (m_array + idx_) T (std::forward<T> (value_) ...);에서 new (m_array + idx_) T (value_ ...);으로 이동 생성자가 필요없이 불필요한 생성/제거 작업을 방지합니다.

EDIT3 : 향후 독자를위한 것입니다. 위와 같이, ~ NoInitArray()는 메모리를 누설합니다. m_array에서 delete를 호출하는 것은 나쁜 소식이며 m_array [0]의 소멸자 (Clang/LLVM에서 호출)입니다 (하지만 지금까지 이해 한대로 보장 할 수는 없습니다, 즉 UB). std :: malloc/std :: free는 갈 길인 것처럼 보이지만 일부는 그렇게하면 모든 지옥이 붕괴되고 다리가 빠질 수 있다고 말합니다.

+1

는 복사 생성자 호출을 계산해야합니다 임시 값 T.를 작성합니다. –

+0

@BenVoigt 복사 생성자가 일반 생성자를 호출하지 않습니까? 이것이 문제가되면 전송이 의도 한대로 작동하지 않는다는 것을 의미합니다. 어떻게하면이 작업을 수행 할 수 있습니까? 즉, 복사하는 대신 오브젝트를 이동할 수 있습니까? – degski

+0

아니요, 복사 생성이 기본 생성자에 연결되지 않습니다. 이동이 아니라 복사가 예상되는 경우 이동 및 복사 생성자의 사용자 정의 버전을 제공하고 각각에 고유 한 메시지를 기록하십시오. –

답변

4

"개체가 한 번 만들어지고 두 번 파괴됩니다."는 사실이 아닙니다. 출력 X move constructed은 하나의 구성으로 포함되어야하므로 구성이 두 번 이루어집니다.

new (m_array + idx_) T (std::forward<T> (value_) ...); 

선은

new (m_array + idx_) T (std::forward<Args&&> (value_)...); 

std::forward<T>(value_) 생성자 T=Int를 호출하고이 임시 객체가 이동하므로 별도의 이동 생성자 호출이 있어야한다.

편집하여 편집 2에서 당신은 더 이상 std::forward없이 라인을 교체합니다. 이 경우, OK,하지만 차이가 등장하면 new T(std::forward<Args&&>(value_)...)는 이동 생성자를 호출 할 때 new T(value_...)는 복사 생성자를 호출 것, std::forward없이이

nia.emplace (0, Int(0)); 

같은 emplace를 호출 할 때.

EDIT-2

그것은 new T(std::forward<Args>(value_)...)해야한다. @ Constantin Baranov에게 감사드립니다.

+0

내 EDIT2에 표시된대로 고마워요.이 경우에는 내가 설명하는대로 작동합니다. 이 (내) 올바른 방법인가요? 아니면 솔루션을 사용하도록 의무화되어있는 사소한 경우입니까? 결과는 동일하게 보입니다. 이동 생성자 인 AFAICS에 대한 호출이 없습니다. – degski

+0

@degski 내 게시물이 업데이트되었습니다. 나는 또한 다른 질문이나 웹 사이트에서'std :: forward'에 대한 더 자세한 정보를 찾아 볼 것을 제안합니다. – neuront

+0

까다로운 물건, 견적 "...하지만 C++로 다리를 날려 버릴 수 있습니다 ..."라는 생각이 듭니다. C++ 11에서는 두 다리를 날려 버릴 수 있습니다. 감사. – degski

4

단계에서 생성자와 소멸자가 호출됩니다. std::forward<T> (value_)new (m_array + idx_) T (std::forward<T> (value_) ...)이 있다고 생각합니다.

std::forward<T>(value_)

+0

나는 하나의 대답만을 받아 들일 수 있습니다. 신경 쓰이는 대답은 약간 더 완전합니다. 그래서 나는 그를 받아 들였습니다. 감사합니다. – degski

관련 문제