9

唯一の非常に不便な注意点はstd::array、組み込みの C 配列のように初期化リストからそのサイズを推測できないことです。そのサイズはテンプレートとして渡す必要があります。

initializer_list を使用して std::array のようなコンテナー (組み込み C 配列の薄いラッパー) を実装することは可能C++11ですか?

std::array とは異なり、イニシャライザ リストから配列のサイズが自動的に推測されるため、より便利です。例えば:

// il_array is the hypothetical container
// automatically deduces its size from the initalizer list 
il_array <int> myarr = {2, 4, 6, 7, 8}; 

初期化子リストが提供されていない場合は、サイズを指定するコンストラクターも提供する必要があります。例えば:

// construct a fixed size array of size 10
il_array <int> myarr2 (10); 

これにより、コンテナーは他の標準コンテナー (vector、deque、list など) とより一貫したものになります。

私の知る限り、T elems [size] などのラップされた C 配列は一定のサイズでなければならず、initializer_list の size() メンバー関数は一定ではないため、これは不可能です。

また、可変個引数テンプレートを使用してそのようなコンテナーを実装できるかどうか疑問に思っていましたが、私が読んだことからは可能ではないと思います。

4

4 に答える 4

3

あなたはここで運が悪いと思います。std::array の大きな利点は、それが POD であり、静的に初期化できることです。

std::initializer_list を受け取るコンストラクターを持つコンテナーがある場合は、値をコピーする必要があります (イニシャライザーへの単なる定数参照である場合を除きますが、これはあまり役に立ちません)。

于 2011-08-13T19:23:57.997 に答える
2

C++0x initializer_list を使用して、std::array のようなコンテナー (組み込み C 配列の薄いラッパー) を実装することは可能ですか?

はい、まあ、ごまかす気がある限り。Mooing Duck が指摘しているように、コンパイラの実装者が許可しない限り、不正行為でさえありません。ただし、十分に近づけることはまだ可能です。初期化子リストと、ラッパーによって隠されている静的配列を使用することは可能です。

これは、個人用ツールボックス用に作成したコードです。重要なのは、配列であってもサイズを完全に無視し、プロバイダー コンテナーに処理させることです。この場合、initializer_list誰が を介してサイズを提供できるstd::distanceので、クライアント側のサイズの明示を回避できます (私が発明したばかりの用語のようです)。

これは「誰でも思いついた」種類のコードであるため、公開に「戻す」ことに問題はありません。実際、Freenode のチャンネルでニックネームを覚えていない専門家からアイデアを得た##c++ので、認識は彼らのためだと思います。

*編集*編:

template <typename T> struct carray {
    // typedefs for iterator. The best seems to be to use std::iterator<std::random_iterator_tag,T,int> here
    ...

    template <size_t N> 
    explicit carray (T (&arr)[N]) 
    : ax(arr), sx(N) {}

    // note the linked article. 
    // This works *only* if the compiler implementor lets you. 
    carray (std::initializer_list<T> X) 
    : ax (X.begin()), sx(std::distance(X.begin(),X.end()) {}

    // YMMV about the rest of the "rule of N":
    // no copy constructor needed -- trivial
    // no destructor needed -- data outscopes the wrapper
    // no assignment operator needed -- trivial

    // container functions, like begin(), end(), size()...

    private:
    T* ax;
    size_t const sx;
};

C++0x モードでの使用法と宣言は非常に単純ですが (Fedora 15 の GCC 4.6 でテストしただけです)、上記の外部リンクに記載されている警告に従って動作するため、明らかに未定義の動作です:

using lpp::carray;
carray<short const> CS = {1, -7, 4, 188};

ただし、コンパイラの実装者が整数の initializer_list を静的配列として実装しない理由がわかりません。あなたの電話。

そのように機能するだけでなく、#ifdefC++0x より前のモードで初期化子コンストラクターを邪魔にならないようにすることができれば、C++0x より前のモードで実際にこれを使用できます。データ配列を独自の変数として事前宣言する必要がありますが、元の意図に最も近いのは IMHO です (また、使用可能であり、スコープの問題などを引き起こさないという利点があります)。(上記のコンパイラと Debian Wheezy の GCC でもテスト済み):

using lpp::carray;
short data[]= {1, -7, 4, 188};
carray<short const> CS (data);

そこには!どこにも「サイズ」パラメータはありません!

初期化子リストが提供されていない場合は、サイズを指定するコンストラクターも提供する必要があります。

申し訳ありませんが、これは私が実装することを考えていない機能の 1 つです。問題は、外部ソース、おそらくアロケーターからメモリを「静的に」割り当てる方法です。ヘルパー functor を介して何らかの方法で実行できると仮定するとallocate、コンストラクターは次のようになります。

explicit carray (size_t N)
: ax(allocate(N)), sx(N) {}

質問が多かれ少なかれ古いことがわかるので、このコードが役立つことを願っています。

于 2011-12-22T20:54:52.310 に答える
1

これはどう?タプル引数の数はコンパイル時に使用できるため、std::tuple代わりに anを使用しました。initializer_list以下のtuple_arrayクラスstd::arrayは、std::tuple. タプルの内容は、Assignコンパイル時に N から 0 まで単純に反復する meta-program を使用して、基になる配列ストレージにコピーされます。最後に、このmake_tuple_array関数は任意の数のパラメーターを受け入れ、tuple_array. 最初の引数の型は、配列の要素型であると見なされます。優れたコンパイラは、RVO を使用して余分なコピーを削除する必要があります。このプログラムは、RVO を使用する g++ 4.4.4 および 4.6.1 で動作します。

#include <array>
#include <tuple>
#include <iostream>

template <size_t I, typename Array, typename Tuple>
struct Assign
{
  static void execute(Array &a, Tuple const & tuple)
  {
    a[I] = std::get<I>(tuple);
    Assign<I-1, Array, Tuple>::execute(a, tuple);
  }
};

template <typename Array, typename Tuple>
struct Assign <0, Array, Tuple>
{
  static void execute(Array &a, Tuple const & tuple)
  {
    a[0] = std::get<0>(tuple);
  }
};

template <class T, size_t N>
class tuple_array : public std::array<T, N>
{
    typedef std::array<T, N> Super;

  public:

    template<typename Tuple>
    tuple_array(Tuple const & tuple)
      : Super()
    {
      Assign<std::tuple_size<Tuple>::value-1, Super, Tuple>::execute(*this, tuple);
    }
};

template <typename... Args>
tuple_array<typename std::tuple_element<0, std::tuple<Args...>>::type, sizeof...(Args)>
make_tuple_array(Args&&... args)
{
  typedef typename std::tuple_element<0, std::tuple<Args...>>::type ArgType;
  typedef tuple_array<ArgType, sizeof...(Args)> TupleArray;
  return TupleArray(std::tuple<Args...>(std::forward<Args>(args)...));
}

int main(void)
{
  auto array = make_tuple_array(10, 20, 30, 40, 50);
  for(size_t i = 0;i < array.size(); ++i)
  {
    std::cout << array[i] << " ";
  }
  std::cout << std::endl;

  return 0;
}
于 2011-12-29T03:47:18.447 に答える
0

この質問は実にシンプルだと思います。initializer_list初期化された のサイズにサイズ変更される型が必要です。

// il_array is the hypothetical container
// automatically deduces its size from the initalizer list 
il_array <int> myarr = {2, 4, 6, 7, 8}; 

これを試して:

// il_array is the hypothetical container
// automatically deduces its size from the initalizer list 
std::initalizer_list<int> myarr = {2, 4, 6, 7, 8}; 

これはコピーを行いますか?最も技術的な意味で...はい。ただし、具体的にはイニシャライザ リストをコピーしても、その内容はコピーされません。したがって、これには数個のポインターのコピーが必要です。また、使用する価値のある C++ コンパイラは、このコピーを何も削除しません。

これで、( 経由で) サイズがわかっている配列ができましたstd::initializer_list::size。ここでの制限は次のとおりです。

  1. サイズはコンパイル時に利用できません。
  2. 配列は可変ではありません。
  3. std::initializer_listかなりベアボーンです。operator[] すらありません。

3番目はおそらく最も厄介です。しかし、それも簡単に修正できます。

template<typename E> class init_array
{
public:
  typedef std::initializer_list<E>::value_type value_type;
  typedef std::initializer_list<E>::reference reference;
  typedef std::initializer_list<E>::const_reference const_reference;
  typedef std::initializer_list<E>::size_type size_type;

  typedef std::initializer_list<E>::iterator iterator;
  typedef std::initializer_list<E>::const_iterator const_iterator;

  init_array(const std::initializer_list<E> &init_list) : m_il(init_list) {}

  init_array() noexcept {}

  size_t size() const noexcept {return m_il.size();}
  const E* begin() const noexcept {return m_il.begin();}
  const E* end() const noexcept {return m_il.end();}

  const E& operator[](size_type n) {return *(m_il.begin() + n);} 
private:
  std::initializer_list m_il;
};

そこには; 問題が解決しました。初期化子リスト コンストラクターにより、初期化子リストから直接作成できることが保証されます。コピーは省略できなくなりましたが、ポインタのペアをコピーしているだけです。

于 2011-12-29T20:35:50.443 に答える