以下のようなcopy関数を作りたい場合、テンプレート引数Tがarrayの時はresizeできない一方で、vectorの時はresizeしなければならない。
これを同じように扱えるようにしたい。具体的にはresize関数の有無で処理を分けたい。
#include <iostream> #include <vector> #include <array>
template<typename T> T copy(T& data) { T tmp; // vectorの時はresizeしないと要素数 0 で落ちる // arrayの時はresizeが存在しないのでコンパイルできない //tmp.resize(3); tmp[0] = data[0]; tmp[1] = data[1]; tmp[2] = data[2]; return tmp; }
int main() { std::vector<float> vec{ 1.0f,1.0f,1.0f }; std::array<float, 3> ary{ 2.0f,2.0f,2.0f }; auto dup_vec = copy(vec); //これをしたい時はresizeを有効に auto dup_ary = copy(ary); //これをしたい時はresizeを無効に printf("%lf %lf %lf\n", dup_vec[0], dup_vec[1], dup_vec[2]); printf("%lf %lf %lf\n", dup_ary[0], dup_ary[1], dup_ary[2]); getchar(); }
やっていることはメンバ関数resizeの存在の判定をして構造体を切り替えているだけ。
#include <type_traits> namespace szl { struct has_resize_impl {
// Tにresizeメンバ関数がある場合はこちらが有効(実体化はしない) // 戻り値がstd::true_typeの関数宣言 template<typename T> static auto check_has_resize (T& o) ->decltype( o.resize(0), std::true_type() );
// Tにresizeがない場合はこちらが有効(実体化はしない) // 戻り値がstd::false_typeの関数宣言 static auto check_has_resize(...) ->decltype( std::false_type() );
}; //プライマリテンプレート template<typename T> struct _myresize_;
//! @brief resizeがある場合呼び出される template<> struct _myresize_<std::true_type> { template<typename T> static void resize_func(T& t, const size_t size) { if (size > t.size()) { t.resize(size); } } };
//!j @brief resizeがない場合に呼び出される。何もしない。 template<> struct _myresize_<std::false_type> { template<typename T> static void resize_func(T& t, const size_t size) { } };
//! @brief resize関数があるオブジェクトに対してのみresizeを実行する //! @param [in,out] obj 配列型のオブジェクト //! @param [in] template<typename T> void resize_if_needed(T& obj, const size_t size) { // resizeを持つオブジェクトなら std::true_type // resizeを持たないオブジェクトなら std::false_type using type = decltype(has_resize_impl::check_has_resize(obj)); // resizeあり/なしバージョンの関数を呼び出す _myresize_<type>::resize_func(obj, size); }
}
#include <iostream> #include <vector> #include <array> #include"has_resize.hpp"
template<typename T> T copy_general(T& data) { T tmp; //型に応じてresizeをする関数としない関数を切り替える szl::resize_if_needed(tmp, 3); tmp[0] = data[0]; tmp[1] = data[1]; tmp[2] = data[2]; return tmp; }
int main() { std::vector<float> vec{ 1.0f,1.0f,1.0f }; std::array<float, 3> ary{ 2.0f,2.0f,2.0f }; auto dup_vec = copy_general(vec); auto dup_ary = copy_general(ary); printf("%lf %lf %lf\n", dup_vec[0], dup_vec[1], dup_vec[2]); printf("%lf %lf %lf\n", dup_ary[0], dup_ary[1], dup_ary[2]); getchar(); }