LoginSignup
4
4

More than 5 years have passed since last update.

container_castをつくった

Posted at

コンテナの中身をそのままに、入れ物のコンテナだけを変えるキャストを思いついたのでガリガリ書いてみました。
requirements:
共通:
 フリーのbegin, endを使ってイテレータが取得できること。
 キャスト先とキャスト元のコンテナのvalue_typeが等しいこと。
コピーする場合: value_typeがコピーコンストラクタを持つこと。
ムーブする場合: value_typeが例外を投げないムーブコンストラクタを持つこと。

ムーブコンストラクタを持っていても、例外を投げるかもしれない場合は例えstd::moveを使って渡そうともコピーが行われます。
また、ムーブされた後のコンテナの要素数はムーブ前と変わりません。中にはムーブされた要素の残骸が残ります。

既知の問題点
1. ムーブの際の例外保証
 すなわち、新しいコンテナのメモリ確保などで例外が発生した場合に要素をちゃんと元のコンテナに戻してくれるか、というのが気になるところなのです。STLは要素がムーブの際に例外を送出しなければやってくれるように記憶していますが、少し自信がありません。

2. value_typeが等しくなければコンパイルエラーになる
 これは安全のためにわざとやったのですが、convertibleであればコンパイルエラーにしなくてもいいような気もする…というのが微妙なところです。

使用例:

struct throwable
{
  throwable() = default;
  throwable(throwable&&) {}
};
struct no_throwable {};

...中略...

  std::vector< throwable > vec;
  vec.resize(3);
  auto list0 = container_cast< std::list< int > >(vec); // copy
  auto list1 = container_cast< std::list< int > >(std::move(vec)); // copy

  std::vector< no_throwable > vec1;
  vec1.resize(3);
  auto list2 = container_cast< std::list< int > >(vec); // copy
  auto list3 = container_cast< std::list< int > >(std::move(vec)); // move

コードのすべては以下になります。

#include <iterator>
#include <type_traits>
#include <utility>

template< class Container1, class Container2,
  typename std::enable_if<
    std::is_same< typename Container1::value_type,
                  typename Container2::value_type >::value,
    std::nullptr_t >::type = nullptr >
Container1 container_cast(const Container2& c)
{
  return {begin(c), end(c)};
}

template< class Container1, class Container2,
  // if each value_type is same type.
  typename std::enable_if<
    std::is_same< typename Container1::value_type,
                  typename Container2::value_type >::value,
    std::nullptr_t >::type = nullptr,
  // if !is_nothrow_move_constructible and copy_constructible.
  typename std::enable_if <
    !std::is_nothrow_move_constructible<
      typename Container1::value_type >::value
    && std::is_copy_constructible<
      typename Container1::value_type >::value,
    std::nullptr_t >::type = nullptr >
Container1 container_cast(Container2&& c)
{ // copy
  return container_cast< Container1 >(c);
}

template< class Container1, class Container2,
  // if each value_type is same type.
  typename std::enable_if<
    std::is_same< typename Container1::value_type,
                  typename Container2::value_type >::value,
    std::nullptr_t >::type = nullptr,
  // if nothrow_move_constructible.
  typename std::enable_if <
    std::is_nothrow_move_constructible<
      typename Container1::value_type >::value,
    std::nullptr_t >::type = nullptr >
Container1 container_cast(Container2&& c)
{ // move
  return {
    std::make_move_iterator(begin(c)),
    std::make_move_iterator(end(c))};
}
4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4