5

假设我有一个既不能移动也不能复制的类型:

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

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

现在给定一个在编译时已知的数字(称为 N),有什么方法可以在堆栈上创建这些“序列”,每个都用数字 0 到 N-1 初始化?foo[N]我会对 C 风格的数组、astd::array< foo, N >或什std::tuple至某种类型的a 感到满意。

我试图避免的是写出:

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

当感觉这是编译器应该能够为我做的事情时。我能想到的最好的方法是使用boost::optional.

boost::optional< foo > f[N];

for( size_t i = 0U; i < N; ++i )
  f[i] = boost::in_place( i );

但这依赖于运行时逻辑,即使所有必需的信息在编译时都可用。另外,我还剩下一些行为类似于指针数组的东西。

4

3 回答 3

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

如果这看起来很麻烦,那就是。但是您可以轻松地将该功能封装在一个类中。

于 2013-04-12T03:59:19.703 回答
1

虽然不是严格意义上的数组,但您可以通过模板递归来完成此操作

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

用法:

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
于 2013-04-12T04:27:42.353 回答
1

就像本杰明林德利的回答一样,但在课堂上打包:

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

现在事情变得简单了一点:

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
于 2015-06-05T20:07:56.227 回答