使用模板是否错误,隐含的假设是定义参数化类型的某些成员函数?

bob*_*obo 5 c++ templates members

说你写的是一个非常糟糕的课程

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 ) ;
  }
} ;
Run Code Online (Sandbox Code Playgroud)

忽略一个事实,即类假定容器将是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_..
Run Code Online (Sandbox Code Playgroud)

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

Xeo*_*Xeo 12

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

我们来看看stdlib 中的任何算法,例如std::copy:

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

这里,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;
}
Run Code Online (Sandbox Code Playgroud)

InIt假设再次支持与上例中相同的操作copy.但是,现在我们也有了UnaryFunction.假设这种类型的对象支持post-fix函数调用表示法,特别是一个参数(一元).进一步假设这个函数调用的参数可以从任何*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 '
}
Run Code Online (Sandbox Code Playgroud)

但是,您也可以使用函数对象 - 用户定义的重载类型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 '
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,我的用户定义类型generate_from可以像函数一样处理,可以将其称为函数.请注意,我作出若干假设Tgenerate_from,即它需要:

  • 可复制的(在ctor中)
  • 后可递增的(在operator())
  • 可复制的(在operator())

  • 这就要提到首次尝试引入概念的失败. (2认同)