Boost.Move に対応した array
※ Boost.Move の入っている revision について書きます
Boost.Interprocess には Boost.Move に対応したコンテナが付属していますが、その中に std::array に相当するものはありません。これは、move constructor などを追加することにより、array が aggregate でなくなってしまうためだと思われます。しかし、そこを強引に解決したくなるのが人情というものです (訳: 暇だった)。
https://gist.github.com/956977
短いコードですが少しだけ解説を加えます。
データの表現方法ですが、配列は使えません。コンストラクタの先頭で必ず value-initialize されるためです。そこで、アラインメントされた生のストレージを用います。
typename boost::aligned_storage< sizeof(T) * (N ? N : 1), boost::alignment_of<T>::value>::type members; // access: pointer data() { return static_cast<pointer>(members.address()); } const_pointer data() const { return static_cast<const_pointer>(members.address()); }
Move constructor と move assignment operator の実装は次のようになります。
array(BOOST_RV_REF(array) x) { boost::uninitialized_move(x.begin(), x.end(), data()); } array &operator=(BOOST_RV_REF(array) x) { boost::move(x.begin(), x.end(), begin()); return *this; }
さらに、aggregate として初期化できなくなった分を補わなくてはならないので、Boost.PP を使って複数のコンストラクタを用意します。
array() { size_type i = 0; try { for (; i < N; ++i) new(data() + i) T(); } catch (...) { for (; i > 0; --i) data()[i - 1].~T(); throw; } } #define BOOST_PP_LOCAL_MACRO(n) \ template <BOOST_PP_ENUM_PARAMS(n, class A)> \ array(BOOST_PP_ENUM(n, RETRO_ARRAY_CONSTRUCTOR_ARGS, ~)) \ { \ BOOST_STATIC_ASSERT(n <= N); \ size_type i = 0; \ try { \ BOOST_PP_REPEAT(n, RETRO_ARRAY_CONSTRUCTOR_FORWARD, ~) \ for (; i < N; ++i) new(data() + i) T(); \ } \ catch (...) { \ for (; i > 0; --i) data()[i - 1].~T(); \ throw; \ } \ } \ /**/ #define RETRO_ARRAY_CONSTRUCTOR_ARGS(z, m, unused) \ BOOST_FWD_REF(BOOST_PP_CAT(A, m)) BOOST_PP_CAT(a, m) \ /**/ #define RETRO_ARRAY_CONSTRUCTOR_FORWARD(z, m, unused) \ new(data() + m) T(boost::forward<BOOST_PP_CAT(A, m)>(BOOST_PP_CAT(a, m))); \ ++i; \ /**/ #define BOOST_PP_LOCAL_LIMITS (1, RETRO_ARRAY_MAX_CONSTRUCTOR_PARAMETERS) #include BOOST_PP_LOCAL_ITERATE() #undef RETRO_ARRAY_CONSTRUCTOR_FORWARD #undef RETRO_ARRAY_CONSTRUCTOR_ARGS
引数を BOOST_FWD_REF で受けているので、その制限が伴います。しかし、多くの場合には問題ないでしょう。
ではサンプルコードを示します。
#include <iostream> #include <boost/interprocess/containers/string.hpp> #include <boost/foreach.hpp> #include "./array.hpp" int main() { using boost::interprocess::string; retro::array<string, 5> a1("Hello", ", ", "world", "!"); // the 5th element is value-initialized retro::array<string, 5> const a2(boost::move(a1)); std::cout << "a1: "; BOOST_FOREACH(string const &s, a1) std::cout << s; // (nothing displayed) std::cout << "\na2: "; BOOST_FOREACH(string const &s, a2) std::cout << s; // Hello, world! }
依然として array