5

假设你写了一个非常糟糕的课程

template <typename T>
class IntFoo
{
  T container ;
public:
  void add( int val )
  {
    // made an assumption that
    // T will have a method ".push_front".
    container.push_front( val ) ;
  }
} ;

忽略该类假设容器将是something<int>的事实,而是注意以下事实

IntFoo< list<int> > listfoo ;
listfoo.add( 500 ) ; // works

IntFoo< vector<int> > intfoo;
//intfoo.add( 500 ) ; // breaks, _but only if this method is called_..

一般来说,像这样调用参数化类型的成员函数可以吗?这是糟糕的设计吗?这个(反)模式有名字吗?

4

1 回答 1

12

这非常好,称为编译时鸭子类型,并在标准库本身的各种地方使用。说真的,如果不假设模板参数支持某些功能,您将如何使用模板做任何有用的事情?

让我们看一下标准库中的任何算法,例如std::copy

template<class InIt, class OutIt>
OutIt copy(InIt first, Init last, OutIt out){
  for(; first != last; ++first)
    *out++ = *first;
  return out;
}

这里,InIt假设一个类型的对象支持operator*()(用于间接)和operator++()推进迭代器。对于 类型的对象,OutIt假设它也支持operator*(),并且operator++(int). 一个普遍的假设也是,从任何收益中返回的任何东西都是*out++可分配的(也就是可转换的) 。*first另一个假设是InItOutIt都是可复制构造的。

另一个使用它的地方是在任何标准容器中。在 C++11 中,当您使用std::vector<T>,时,当且仅当您使用任何需要副本的成员函数时,才T需要是可复制构造的。

所有这些都使得用户定义的类型可以被视为与内置类型相同,即它们是该语言的一等公民。让我们再看一些算法,即那些采用要应用于范围的回调的算法:

template<class InIt, class UnaryFunction>
InIt for_each(InIt first, InIt last, UnaryFunction f){
  for(; first != last; ++first)
    f(*first);
  return first;
}

InIt假设再次支持与copy上面示例中相同的操作。但是,现在我们也有UnaryFunction. 假定这种类型的对象支持后缀函数调用表示法,特别是带有一个参数(一元)。进一步假设这个函数调用的参数可以从任何*first产量转换。

使用该算法的典型示例是使用普通函数:

void print(int i){ std::cout << i << " "; }

int main(){
  std::vector<int> v(5); // 5 ints
  for(unsigned i=0; i < v.size(); ++i)
    v[i] = i;
  std::for_each(v.begin(), v.end(), print); // prints '0 1 2 3 4 '
}

但是,您也可以为此使用函数对象- 一种重载的用户定义类型operator()

template<class T>
struct generate_from{
  generate_from(T first) : _acc(first) {}
  T _acc;
  void operator()(T& val){ val = _acc++; }
};

int main(){
  std::vector<int> v(5); // 5 ints
  // yes, there is std::iota. shush, you.
  std::for_each(v.begin(), v.end(), generate_from<int>(0)); // fills 'v' with [0..4]
  std::for_each(v.begin(), v.end(), print); // prints '0 1 2 3 4 '
}

如您所见,我的用户定义类型generate_from可以完全像函数一样对待,它可以像函数一样被调用。请注意,我对Tin做了几个假设generate_from,即它需要是:

  • 可复制构造(在 ctor 中)
  • 后增量(在 中operator()
  • 可复制分配(在 中operator()
于 2012-10-03T18:38:26.223 回答