コンテナの中身をそのままに、入れ物のコンテナだけを変えるキャストを思いついたのでガリガリ書いてみました。
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))};
}