スポンサーリンク

| キーワード:

std::vectorもstd::arrayもローカル変数の型として同じように扱うためにresizeの呼び出しを切り替える

問題

以下のような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の存在の判定をして構造体を切り替えているだけ。

has_resize.hpp

#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);
  }
}

copy_generalの実装と使い方

#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();

}

コメントを残す

メールアドレスが公開されることはありません。

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


この記事のトラックバックURL: