簡體   English   中英

將參數傳遞給“類似數組”的容器構造函數

[英]Passing arguments to “array-like” container constructor

背景

我正在使用嵌入式平台,但有以下限制:

  • 沒有堆
  • 沒有Boost庫
  • C ++ 11 支撐

我過去曾經多次處理過以下問題:

創建一個類類型為T的數組,其中T沒有默認構造函數

該項目最近才添加了C ++ 11支持,到目前為止,每次我必須處理這個問題時,我一直在使用ad-hoc解決方案。 既然C ++ 11已經可用,我想我會嘗試制作一個更通用的解決方案。

解決方案嘗試

我復制了一個std :: aligned_storage的例子來為我的數組類型提出框架。 結果如下:

#include <type_traits>

template<class T, size_t N>
class Array {
  // Provide aligned storage for N objects of type T
  typename std::aligned_storage<sizeof(T), alignof(T)>::type data[N];

public:
  // Build N objects of type T in the aligned storage using default CTORs
  Array()
  {
    for(auto index = 0; index < N; ++index)
      new(data + index) T();
  }

  const T& operator[](size_t pos) const
  {
    return *reinterpret_cast<const T*>(data + pos);
  }

  // Other methods consistent with std::array API go here
};

這是一個基本類型 - 如果T是默認可構造的,則只能編譯Array<T,N> 我對模板參數打包不是很熟悉,但是看一些例子讓我得到以下結論:

template<typename ...Args>
Array(Args&&... args) 
{
  for(auto index = 0; index < N; ++index)
    new(data + index) T(args...);
}

這絕對是朝着正確方向邁出的一步。 如果傳遞的參數與T的構造函數匹配Array<T,N>現在編譯。

我唯一剩下的問題是構造一個Array<T,N> ,其中數組中的不同元素具有不同的構造函數參數。 我想我可以把它分成兩種情況:

1 - 用戶指定參數

這是我對CTOR的抨擊:

template<typename U>
Array(std::initializer_list<U> initializers)
{
  // Need to handle mismatch in size between arg and array
  size_t index = 0;
  for(auto arg : initializers) {
    new(data + index) T(arg);
    index++;
  }
}

除了需要處理數組和初始化列表之間的維度不匹配之外,這似乎工作正常,但有許多方法可以處理那些不重要的方法。 這是一個例子:

struct Foo {
  explicit Foo(int i) {}
};

void bar() {
  // foos[0] == Foo(0)
  // foos[1] == Foo(1)
  // ..etc
  Array<Foo,10> foos {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
}

2 - 參數遵循模式

在我之前的示例中, foos使用遞增列表進行初始化,類似於std::iota 理想情況下,我想支持以下內容,其中range(int)返回可以初始化數組的SOMETHING。

// One of these should initialize foos with parameters returned by range(10)
Array<Foo,10> foosA = range(10);
Array<Foo,10> foosB {range(10)};
Array<Foo,10> foosC = {range(10)};
Array<Foo,10> foosD(range(10));

谷歌搜索告訴我std::initializer_list不是一個“普通”容器,所以我認為我沒有辦法讓range(int)根據函數參數返回一個std::initializer_list

同樣,這里有幾個選項:

  • 在運行時指定的參數(函數返回?)
  • 編譯時指定的參數( constexpr函數返回?模板?)

問題

  1. 到目前為止,此解決方案是否存在任何問題?
  2. 有沒有人有建議生成構造函數參數? 除了硬編碼std::initializer_list ,我無法在運行時或編譯時想到解決方案,因此歡迎任何想法。

如果我正確地理解你的問題,我也偶然發現了std :: array關於元素構造的完全不靈活性,有利於聚合初始化(以及缺少具有靈活元素構造選項的靜態分配容器)。 我想出的最好的方法是創建一個類似自定義數組的容器,它接受一個迭代器來構造它的元素。 這是完全靈活的解決方案:

  • 適用於固定大小和動態大小的容器
  • 可以將不同或相同的參數傳遞給元素構造函數
  • 可以使用一個或多個(元組分段構造)參數調用構造函數,甚至可以調用不同元素的不同構造函數(具有控制反轉)

對於你的例子,它將是:

const size_t SIZE = 10;

std::array<int, SIZE> params;
for (size_t c = 0; c < SIZE; c++) {
    params[c] = c;
}

Array<Foo, SIZE> foos(iterator_construct, &params[0]); //iterator_construct is a special tag to call specific constructor
// also, we are able to pass a pointer as iterator, since it has both increment and dereference operators

注意:您可以通過使用自定義迭代器類完全跳過參數數組分配,該類從動態位置計算它的值。

對於多參數構造函數,它將是:

const size_t SIZE = 10;

std::array<std::tuple<int, float>, SIZE> params; // will call Foo(int, float)
for (size_t c = 0; c < SIZE; c++) {
    params[c] = std::make_tuple(c, 1.0f);
}

Array<Foo, SIZE> foos(iterator_construct, piecewise_construct, &params[0]);

具體的實現示例是一個很大的代碼,所以如果你想了解除了一般想法之外的實現細節的更多見解,請告訴我 - 我會更新我的答案。

我用的是工廠lambda。

lambda獲取指向構造位置和索引的指針,並負責構造。

這使得復制/移動也易於編寫,這是一個好兆頭。

template<class T, std::size_t N>
struct my_array {
  T* data() { return (T*)&buffer; }
  T const* data() const { return (T const*)&buffer; }

  // basic random-access container operations:
  T* begin() { return data(); }
  T const* begin() const { return data(); }
  T* end() { return data()+N; }
  T const* end() const { return data()+N; }
  T& operator[](std::size_t i){ return *(begin()+i); }
  T const& operator[](std::size_t i)const{ return *(begin()+i); }

  // useful utility:
  bool empty() const { return N!=0; }
  T& front() { return *begin(); }
  T const& front() const { return *begin(); }
  T& back() { return *(end()-1); }
  T const& back() const { return *(end()-1); }
  std::size_t size() const { return N; }

  // construct from function object:
  template<class Factory,
    typename std::enable_if<!std::is_same<std::decay_t<Factory>, my_array>::value, int> =0
  >
  my_array( Factory&& factory ) {
    std::size_t i = 0;
    try {
      for(; i < N; ++i) {
        factory( (void*)(data()+i), i );
      }
    } catch(...) {
      // throw during construction.  Unroll creation, and rethrow:
      for(std::size_t j = 0; j < i; ++j) {
        (data()+i-j-1)->~T();
      }
      throw;
    }
  }
  // other constructors, in terms of above naturally:
  my_array():
    my_array( [](void* ptr, std::size_t) {
      new(ptr) T();
    } )
  {}
  my_array(my_array&& o):
    my_array( [&](void* ptr, std::size_t i) {
      new(ptr) T( std::move(o[i]) );
    } )
  {}
  my_array(my_array const& o):
    my_array( [&](void* ptr, std::size_t i) {
      new(ptr) T( o[i] );
    } )
  {}
  my_array& operator=(my_array&& o) {
    for (std::size_t i = 0; i < N; ++i)
      (*this)[i] = std::move(o[i]);
    return *this;
  }
  my_array& operator=(my_array const& o) {
    for (std::size_t i = 0; i < N; ++i)
      (*this)[i] = o[i];
    return *this;
  }
private:
  using storage = typename std::aligned_storage< sizeof(T)*N, alignof(T) >::type;
  storage buffer;
};

它定義了my_array() ,但只有在嘗試編譯時才會編譯。

支持初始化列表相對容易。 當il不夠長或太長時,決定做什么很難。 我想你可能想要:

template<class Fail>
my_array( std::initializer_list<T> il, Fail&& fail ):
  my_array( [&](void* ptr, std::size_t i) {
    if (i < il.size()) new(ptr) T(il[i]);
    else fail(ptr, i);
  } )
{}

這要求你傳遞“失敗時該做什么”。 我們可以默認通過添加:

template<class WhatToThrow>
struct throw_when_called {
  template<class...Args>
  void operator()(Args&&...)const {
    throw WhatToThrow{"when called"};
  }
};

struct list_too_short:std::length_error {
  list_too_short():std::length_error("list too short") {}
};
template<class Fail=ThrowWhenCalled<list_too_short>>
my_array( std::initializer_list<T> il, Fail&& fail={} ):
  my_array( [&](void* ptr, std::size_t i) {
    if (i < il.size()) new(ptr) T(il[i]);
    else fail(ptr, i);
  } )
{}

如果我寫得正確,會使一個太短的初始化列表導致一個有意義的拋出消息。 在您的平台上,如果沒有例外,您可以exit(-1)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM