3

我正在尝试编写一个函数,该函数采用两个具有相同包含类型的容器,例如两个std::vector<int>s 或 astd::list<int>和 a std::vector<int>。(但不是 astd::vector<int>和 a 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: error: non-template 'instance_wrapper' used as template
prog.cpp:25:24: note: use 'C1::template instance_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,因此您永远无法传递其中两个。另外,我不会调用 function move,因为您将第一个容器的内容添加到第二个容器中。

也就是说,std::moveC++11 中有一个重载,它采用三个迭代器,并将第一个标记的内容也移动到作为第三个参数传递的输出迭代器中。因此,您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 回答