将迭代器传递给类函数

5

我正在尝试创建一个名为"CrazyBucket<T>"的新模板类,它必须保存多个类型为T的值。

我想为这个类创建一个构造函数,它可以接受任何2个前向迭代器作为参数,并将这些值(类型为T)复制到类实例中。

构造函数:

CrazyBucket< T >::CrazyBucket( iterator< forward_iterator_tag, T > start,  iterator< forward_iterator_tag, T > end )
{ ... }

但是当我尝试使用以下方式调用它时:
vector< int > vec;
vec.push_back( 4 );
CrazyBucket< int > bucket( vec.begin(), vec.end() );

我得到了以下错误:
候选构造函数不可行:第一个参数没有已知的从'iterator'(即'__wrap_iter')转换为'std::iterator'。
非常感谢您对如何定义我的构造函数的帮助。
提前致谢。

1
为什么不使用类似这样的非常简单的东西:http://ideone.com/EU9FTK(如果可以接受,我将把它发布为答案)。 - PaulMcKenzie
@NickZavaritsky - 感谢您的指引。也许我没有正确理解这篇帖子,但那个发帖者想要限制传入的迭代器类型吗?它并没有讨论迭代器指向的数据类型(在我的例子中是int)。 - Akanes
@PaulMcKenzie 我认为你应该直接发布答案,因为那是正确的做法。 - Angew is no longer proud of SO
@PaulMcKenzie - 有没有办法确保 Iter 只指向类模板类型 < T > 的迭代器(即只指向 < T > 类型的对象)?其中 < T > 是该类的模板类型。 - Akanes
1
@WhozCraig @NickZavaritsky 我不介意迭代器的级别(抱歉,我不知道技术术语)-即以下任何一种都可以:前向、双向、随机访问。因此,我选择了最低公共分母的前向迭代器。但是,我确实想限制迭代器“指向”的对象类型。也就是说,确保它们指向类型为<T>的对象。 - Akanes
显示剩余3条评论
2个回答

2

你可以使用SFINAE来排除不匹配的类型,我认为这种方式接近于你想要的。

#include <iostream>
#include <iterator>
#include <vector>

template<class T>
class CrazyBucket
{
public:
    template<class It, typename = typename std::enable_if<
        std::is_same< typename std::iterator_traits<It>::value_type,T>::value>::type>
    CrazyBucket(It beg, It end)
    {
        std::cout << __PRETTY_FUNCTION__ << '\n';
    }
};

int main()
{
    std::vector<int> vInt;
    CrazyBucket<int> cbOK(vInt.begin(), vInt.end());

    int ar[10];
    CrazyBucket<int> cbAlsoOK(ar, ar+10);

    // uncomment for failure test case.
    //std::vector<double> vDbl;
    //CrazyBucket<int> cbFail(vDbl.begin(), vDbl.end());
}

还可以使用静态断言来实现:

template<class It>
CrazyBucket(It beg, It end)
{
    static_assert(std::is_same<T, typename std::iterator_traits<It>::value_type>::value,
       "failed to match iterator value type");
    std::cout << __PRETTY_FUNCTION__ << '\n';
}

“Either”是一种限制条件,需要知道这可能不是你想要的最终目标。例如,short的迭代自然会以int存储而不会丢失数据,但这种SFINAE会将其抛弃。这也可以通过更多的扩展来克服,但到那时,我认为你需要考虑它是否真的值得。

无论如何,祝你好运。


那真的非常有帮助和信息量!我对第二个代码示例进行了小小的修改,以确保迭代器的类型也得到满足(在这种情况下,“It”至少是前向迭代器)。 - Akanes
如果有人需要在运行时执行此操作,#include <typeinfo>if( typeid( typename std::iterator_traits< IterType >::value_type ) != typeid( T ) ) 将完成任务。 - Akanes

1

真的需要构造函数吗?我认为问题在于你需要一个专门的构造函数,而这在标准中是不可能的。如果你可以将初始化推迟到成员函数中,则以下方法将起作用:

template<class T>
struct CrazyContainer {

  template<class U>
  void init(U first,U last) {
    for(auto it=first;it!=last;it++) {
      // do stuff with 'it'
    }
  }
};

main() {
  std::vector<int> vec;

  CrazyContainer<int> f;
  f.init(vec.begin(),vec.end());
}

我希望能看到是否有其他人可以想出一种通过构造函数实现这个目标的方法。
编辑: 感谢Sebastian指出模板化构造函数与模板化方法同样有效。
template<class T>
struct CrazyContainer {

  template<class U>
  CrazyContainer(U first,U last) {
    for(auto it=first;it!=last;it++) {
      // do stuff
    }
  }
};


main() {
  std::vector<int> v;
  std::set<int> s;

  CrazyContainer<int> cv(v.begin(),v.end());
  CrazyContainer<int> cs(s.begin(),s.end());
}

你可以使用完全相同的代码作为构造函数,只需将 void init 替换为 CrazyContainer。你认为这样做不起作用的原因是什么? - Sebastian Redl
@Sebastian,你说得对,谢谢你指出来。我会修改答案以显示这一点。 - Andy Brown

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接