2013-04-12 22 views
5

ı hareketli ne de copyable özellikte olmayan bir tür var diyelim: Artık derleme zamanında (N diyoruz) bilinen bir numara verilirTaşınamaz, kopyalanamaz nesneler dizisi nasıl başlatılır?

struct foo 
{ 
    explicit foo(size_t){} 
    ~foo(){} 

    foo(foo const &) = delete; 
    foo(foo &&) = delete; 
    foo& operator=(foo const &) = delete; 
    foo& operator=(foo &) = delete; 
}; 

, bunlardan bir "dizisini" oluşturabilir herhangi bir yolu yoktur her biri 0 ile N-1 ile sıfırlanan yığın üzerinde? Bir C-stili dizi foo[N], bir std::array< foo, N > veya bir tür bile bir std::tuple memnun olurum. Ben kaçınmaya çalışıyorum ne

dışarı yazıyor:

foo f0(0), f1(1), ... fNminus1(N-1); 

böyle hissediyor derleyici benim için yapmaya muktedir gereken bir şeydir. Gelebildiğim en iyi şey boost::optional kullanıyor.

Ama hepsi gerekli bilgiler derleme sırasında kullanılabilir olmasına karşın çalışma zamanı mantığına dayanır. Ayrıca, bir dizi işaretçi gibi davranan bir şeyle kaldım.

cevap

3
// create a type with the proper alignment 
typedef std::aligned_storage<sizeof(foo), std::alignment_of<foo>::value>::type buffer_type; 

const int N = 10; 
// create an array of uninitialized raw data 
buffer_type storage_buffer[N]; 

// initialize each foo object with placement new 
for (size_t i=0; i<N; ++i) 
    new (storage_buffer + i) foo(i); 

foo * fp = (foo*)(&storage_buffer); 
// access your foo objects via fp 


// you must manually call the destructor of each object 
for (size_t i=0; i<N; ++i) 
    fp[i].~foo(); 

Bu çok güçlük gibi görünüyorsa, öyle. Ancak bu işlevi bir sınıfta kolayca kapatabilirsiniz.

1

kesinlikle bir dizi, sen tür şablon özyineleme ile gerçekleştirebilirsiniz olmasa da

template< typename T, size_t N > 
struct type_array : public type_array< T, N-1 > { 
    // this is the Nth element 
    T elem; 
    // it is constructed with N 
    type_array() : elem(N) {} 

    // member function to return the Nth element 
    T & get(size_t n) { 
     if (n == N) { 
      return elem; 
     } else { 
      return type_array< T, N-1 >::get(n); 
     } 
    } 
}; 

// base case when N == 0 
template< typename T > 
struct type_array<T, 0> { 
    T elem; 
    type_array() : elem(0) {} 
    T & get(size_t n) { 
     return elem; 
    } 
}; 

Kullanımı:

type_array< foo, 100 > foo_array; // construct 100 foos 
foo_array.get(1);     // foo with n == 1 
foo_array.get(2);     // foo with n == 2 
1
Benjamin Lindley gelen cevap gibi

ancak bir sınıfta paketlenmiş:

#include <type_traits> 
#include <utility> 
#include <new> 

template<typename T> 
class uninitialized { 
public: 
    constexpr uninitialized() { } 

    ~uninitialized() { 
    get().~T(); 
    } 

    explicit uninitialized(const uninitialized& other) { 
    construct(other); 
    } 

    explicit uninitialized(uninitialized&& other) { 
    construct(std::move(other)); 
    } 

    template<class... Args> 
    explicit uninitialized(Args&&... args) { 
    construct(std::forward<Args>(args)...); 
    } 

    template<class... Args> 
    void construct(Args&&... args) noexcept { 
    static_assert(std::is_nothrow_constructible<T, Args...>::value, "constructor should not throw!"); 
    ::new(getPointer()) T (std::forward<Args>(args)...); 
    } 

    uninitialized& operator = (const T& t) { 
    get() = t; 
    return *this; 
    } 

    uninitialized& operator = (T&& t) { 
    get() = std::move(t); 
    return *this; 
    } 

    T* operator ->() { return getPointer(); }  
    T& operator *() { return get(); }  
    T* operator &() { return getPointer(); }  
    T* getPointer() { return reinterpret_cast<T*>(&data); }  
    T& get() { return *reinterpret_cast<T*>(&data); } 

    const T* operator ->() const { return getPointer(); }  
    const T& operator *() const { return get(); }  
    const T* operator &() const { return getPointer(); }  
    const T* getPointer() const { return reinterpret_cast<const T*>(&data); }  
    const T& get() const { return *reinterpret_cast<const T*>(&data); } 

private: 
    std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type data; 
}; 

Şimdi işler biraz daha kolay:

uninitialized<foo> f[N]; 

for (size_t i = 0; i < N; ++i) 
    f[i].construct(i); 

for (const auto& fooref : f) 
    fooref->bar(); 

// foo::~foo is called for you 
+0

Korkarım ki bunu reddetmeliyim. Sorun şu ki, eğer 'yapı' bir istisna atarsa, hiçbir zaman inşa edilmemiş bir nesnenin yıkıcısını çağırırsınız. –

+0

@DavidStone Evet, bu bir sorun. Kurucu atmamalı. Bir static_assert ekledim. Benjamin Lindley'in cevabında, kurucu başarısız olduğunda, hiçbir yıkıcı aramaz. – R1tschY

+0

Yükseltme olarak değiştirildi –

İlgili konular