3

同じ包含型の2つのコンテナー、たとえば2つstd::vector<int>のs、またはastd::list<int>とaを受け取る関数を作成しようとしていstd::vector<int>ます。(しかし、astd::vector<int>std::vector<double>!)ではありません

それがどのように行われるべきかについて私はよくわからないので、私は最初にテストプログラムを書くことにしました:

#include <iostream>
#include <vector>
#include <list>
#include <algorithm>

struct vector_wrapper
{
  template <typename T>
  struct instance_wrapper
  {
    typedef typename std::vector<T> instance;
  };
};

struct list_wrapper
{
  template <typename T>
  struct instance_wrapper
  {
    typedef typename std::list<T> instance;
  };
};

template <typename T, typename C1, typename C2>
void move(typename C1::instance_wrapper<T>::instance& c1, typename C2::instance_wrapper<T>::instance& c2) // line 29
{
  while (c1.size() > 0)
  {
    c2.push_front(c1.back());
    c1.pop_back();
  }
}

int main()
{
  std::vector<int> v;
  std::list  <int> l;

  v.reserve(10);
  for (int i = 0; i < 10; ++i)
    v.push_back(i);

  move<int, vector_wrapper, list_wrapper>(v, l);

  std::for_each(l.begin(), l.end(),
    [] (int i) { std::cout << i << " "; }
  );

  std::cout << std::endl;

  return 0;
}

-std=c++11このコードでは、フラグを使用して、g++4.7で次のコンパイル時エラーが発生します。

metaclass.cpp:29:24: error: non-template 'instance_wrapper' used as template
... more ...

instance_wrapperコンパイラがテンプレートとして正しく識別されないのはなぜですか?

4

3 に答える 3

6

コンパイラはすでに何が悪いのかを教えてくれました(ideoneからのエラー):

prog.cpp:25:24:エラー:テンプレートとして使用される非テンプレート'instance_wrapper'
prog.cpp:25:24:注:テンプレートであることを示すには、'C1 ::templateinstance_wrapper'を使用してください

C1::template instance_wrapper-の代わりに使用し、同様に、 :C1::instance_wrapperに対して同じことを行います。C2::instance_wrapper

template <typename T, typename C1, typename C2>
void move(typename C1::template instance_wrapper<T>::instance& c1, 
    typename C2::template instance_wrapper<T>::instance& c2)
{
    // ...

これC1は、がテンプレートであり、コンパイラがそれをテンプレートとして推測できずinstance_wrapper、非テンプレート型として扱うためです。

コンパイラの出力をすべて読んでください。行ごとだけではありません。多くの場合、コンパイラは、この場合のように、前の行または次の行のいずれかで何が問題になっているのかを示します。

于 2012-06-30T23:25:59.363 に答える
3

これは、引数の推論を無効にしてクライアントに指定させる奇妙なラッパーを必要としない、C++03で動作するクリーンでシンプルなSFINAEだけのより良いソリューションです。

#include <type_traits> // or Boost equivalent

// a little helper struct to keep the 
// function as clutter-free as possible
template<class C1, class C2, class T = void>
struct enable_if_same_value_type
  : std::enable_if<std::is_same<typename C1::value_type,
        typename C2::value_type>::value, T>
{
};

template<class C1, class C2>
typename enable_if_same_value_type<C1, C2>::type
move(C1& source, C2& target){
  /* ... */
}

push_frontでサポートされていないため、コードはあまり一般的ではないことに注意してください。std::vectorそのため、2つを渡すことはできません。moveまた、最初のコンテナのコンテンツを2番目のコンテナの前に追加しているため、関数は呼び出さないことにします。

とは言うstd::moveものの、C ++ 11にはオーバーロードがあり、3つのイテレーターを取り、最初のイテレーターでマークされたコンテンツを3番目のパラメーターとして渡される出力イテレーターに移動します。そのため、main次のように書き直すことができます。

#include <algorithm> // algorithmic move
#include <vector>
#include <list>
#include <iterator> // front_inserter
#include <iostream>

int main(){
  std::vector<int> v;
  std::list<int> l;

  v.reserve(10);
  for (unsigned i = 0; i < 10; ++i)
    v.push_back(i);

  // I used 'rbegin'  and 'rend' so the order stays the same
  std::move(v.rbegin(), v.rend(), std::front_inserter(l));

  std::copy(l.begin(), l.end(), std::ostream_iterator<int>(std::cout, " "));
}

Ideoneの実例。

于 2012-06-30T23:44:15.900 に答える
2

今後の参考のために、これをより簡単に行うことができます。

#include <type_traits>

template<typename T, typename A1, typename A2, template <typename, typename> class Cont1, template<typename, typename> class Cont2>
void move(Cont1<T, A1>& from, Cont2<T, A2>& to) {
    while (!from.empty()) { // use !empty(), not size()
        to.push_front(from.back());
        from.pop_back();
    }
}

std::vector<int> v;
std::list<int> l;

move(v, l); // and no need to specify types
于 2012-06-30T23:45:10.677 に答える