2013-04-12 33 views
5

Hãy nói rằng tôi có một loại mà không phải là di chuyển cũng không copyable:Làm thế nào để khởi tạo một chuỗi các đối tượng không thể di chuyển, không thể sao chép?

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

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

Bây giờ đưa ra một số được biết đến tại thời gian biên dịch (gọi nó là N), có cách nào mà tôi có thể tạo ra một "chuỗi" các trên stack với mỗi một khởi tạo với số 0 đến N-1? Tôi sẽ hài lòng với một mảng kiểu C foo[N], một std::array< foo, N > hoặc thậm chí là std::tuple của một số loại.

Những gì tôi đang cố gắng để tránh được viết ra:

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

khi nó cảm thấy như thế này là một cái gì đó trình biên dịch sẽ có thể làm cho tôi. Điều tốt nhất tôi có thể nghĩ ra là sử dụng boost::optional.

Nhưng dựa trên logic thời gian chạy mặc dù tất cả thông tin bắt buộc có sẵn tại thời gian biên dịch. Ngoài ra, tôi còn lại với một cái gì đó hoạt động giống như một mảng con trỏ.

Trả lời

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(); 

Nếu điều đó có vẻ như rất nhiều rắc rối, đó là. Nhưng bạn có thể dễ dàng đóng gói chức năng đó trong một lớp học.

1

Mặc dù không hoàn toàn một mảng, bạn có thể loại thực hiện điều này với mẫu đệ quy

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; 
    } 
}; 

Cách sử dụng:

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

Giống như câu trả lời từ Benjamin Lindley, nhưng đóng gói trong một lớp:

#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; 
}; 

Bây giờ mọi thứ trở nên dễ dàng hơn một chút:

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

Tôi sợ tôi phải bỏ phiếu này xuống. Vấn đề ở đây là nếu 'construct' ném một ngoại lệ, bạn sẽ gọi hàm hủy của đối tượng chưa bao giờ được xây dựng. –

+0

@DavidStone Có vấn đề này. Các nhà xây dựng không nên ném. Tôi đã thêm một 'static_assert'. Trong câu trả lời từ Benjamin Lindley, khi constructor thất bại, không có destructor nào được gọi. – R1tschY

+0

Đã thay đổi thành upvote –