在自定义向量类中调用了错误的构造函数

Wrong constructor called in custom vector class

本文关键字:错误 构造函数 调用 自定义 向量      更新时间:2023-10-16

我是C++的新手,作为一个学习练习,我正在创建自己的简单向量类。

它似乎工作得很好,除了当我创建一个Vec对象试图使用
Vec(size_type n,const T&val=T(((构造函数,它希望使用模板化的构造函数,该构造函数应该使用两个迭代器/指针,并给我错误。

我相信我理解为什么会发生这种情况,但我想不出一种方法,在不改变类的使用方式的情况下,让它变得不那么模糊。我希望使用与std::vector相同的方法。

如何调用正确的构造函数?std::矢量如何避免这个问题?

Vec.h

//#includes: <algorithm> <cstddef> <memory>
template <class T> class Vec
{
public:
//typedefs for iterator, size_type....
//just used T* for iterator
    Vec() { create(); }
    Vec(const Vec& v) { create(v.begin(), v.end()); }
    explicit Vec(size_type n, const T& val = T()) { create(n, val); }
    template <class InputIt>
    Vec(InputIt first, InputIt last) { create(first, last); }
    ~Vec() { uncreate(); }
    T& operator[](size_type i) { return data[i]; }
    const T& operator[](size_type i) const { return data[i]; }
    Vec& operator=(const Vec&);
    //functions such as begin(), size(), etc...
private:
    iterator data;
    iterator avail;
    iterator limit;
    std::allocator<T> alloc;
    void create();
    void create(size_type, const T&);
    template <class InputIt>
    void create(InputIt first, InputIt last)
    {
        data = alloc.allocate(last - first);
        limit = avail = std::uninitialized_copy(first, last, data);
    }
    void uncreate();
    void grow();
    void unchecked_append(const T&);
};
template <class T>
Vec<T>& Vec<T>::operator=(const Vec& rhs)
{
    if (&rhs != this)
    {
        uncreate();
        create(rhs.begin(), rhs.end());
    }
    return *this;
}
template <class T> void Vec<T>::create()
{
    data = avail = limit = 0;
}
template <class T> void Vec<T>::create(size_type n, const T& val)
{
    data = alloc.allocate(n);
    limit = avail = data + n;
    std::uninitialized_fill(data, limit, val);
}
template <class T>
void Vec<T>::create(const_iterator i, const_iterator j)
{
    data = alloc.allocate(j - i);
    limit = avail = std::uninitialized_copy(i, j, data);
}
template <class T> void Vec<T>::uncreate()
{
    if (data)
    {
        iterator it = avail;
        while (it != data)
            alloc.destroy(--it);
        alloc.deallocate(data, limit - data);
    }
    data = limit = avail = 0;
}
//left out what I didn't think was relevant

除了我上面提到的情况外,它似乎运行良好。

main.cpp

#include "Vec.h"
main()
{
    Vec<int> v1(10, 100);
}

我收到错误:

||===编译:在Vec类中调试(编译器:GNU GCC编译器(===|c: \program files\codeblocks\mingw\bin\。。\lib\gcc\mingw32\4.7.1\include\c++\bits\stl_uniinitialized.h||在'_ForwardIterator std::uninitialized_copy(_InputIterator,_InputIt erator,_ForwardIt erator(的实例化中[其中_InputIterator=int;_Forward Iterator=int*]':|F: \CBProjects\Accelerated C++\ Programming with Classes\Vec Class\Vec.h|58|必需自'void Vec::create(InputIt,InputIt([with InputIt=int;T=int]'|F: \CBProjects\Accelerated C++\ Programming with Classes\Vec Class\Vec.h|24|必需自"Vec::Vec(InputIt,InputIt([with InputIt=int;T=int]"|F: \CBProjects\Accelerated C++\ Programming with Classes\Vec Class\main.cpp|9|此处为必填项|c: \program files\codeblocks\mingw\bin\。。\lib\gcc\mingw32\4.7.1\include\c++\bits\stl_uniinitialized.h|113|错误:"struct-std::iterator_traits"中没有名为"value_type"的类型|||===生成失败:1个错误,4个警告(0分钟,0秒(===|

这是我第一次在这里提问。我希望我问得恰当,并提供了足够的信息。如果我在这个过程中犯了错误,请告诉我,并感谢你能提供的任何帮助。

编辑:

对于任何想知道我最终做了什么的人。我更改了模板化的构造函数,这样它就不会被算术参数调用(因为它只适用于迭代器/指针(。

template <class InputIt>
Vec(InputIt first, InputIt last,
    typename std::enable_if<!std::is_arithmetic<InputIt>::value>::type* = 0) {create(first, last); }  

它看起来很好用,但实际上我对enable_if不是很熟悉,所以如果有人知道这种方法的任何危险,请告诉我。

之所以选择模板化构造函数,是因为它与参数更匹配。

查看向量构造函数的文档,您将看到一个注意事项,即只有当参数满足InputIterator的要求时,该构造函数才会参与重载解析。这通常是通过使用SFINAE禁用过载来实现的。以下是libcpp如何实现这一点。我试着去掉一些不必要的部分,并调整了命名约定,以提高可读性。

在实际实现中,此构造函数的常见定义如下:

template <class InputIterator>
vector(InputIterator first, InputIterator last,
       typename enable_if<is_input_iterator  <InputIterator>::value>::type* = 0);
template <class InputIterator>
vector(InputIterator first, InputIterator last, const allocator_type& a,
       typename enable_if<is_input_iterator  <InputIterator>::value>::type* = 0);

我去掉了进一步区分InputIteratorForwardIterator的不必要部分。SFINAE的重要部分是enable_if及其参数。

以下是is_input_iterator:的必要定义

template <class Tp, class Up,
          bool = has_iterator_category<iterator_traits<Tp> >::value>
struct has_iterator_category_convertible_to
    // either integral_constant<bool, false> or integral_constant<bool, true>
  : public integral_constant<
      bool, 
      is_convertible<typename iterator_traits<Tp>::iterator_category, Up>::value
    >
{};
// specialization for has_iterator_category<iterator_traits<Tp> >::value == false
template <class Tp, class Up>
struct has_iterator_category_convertible_to<Tp, Up, false> : public false_type {};
template <class Tp>
struct is_input_iterator 
  : public has_iterator_category_convertible_to<Tp, input_iterator_tag> {};

它的本质是检查给定类型是否具有可以转换为input_iterator_tagiterator_category标记。

has_iterator_category更加晦涩难懂:

template <class Tp>
struct has_iterator_category
{
private:
    struct two {char lx; char lxx;};
    template <class Up> static two test(...);
    template <class Up> static char test(typename Up::iterator_category* = 0);
public:
    static const bool value = sizeof(test<Tp>(0)) == 1;
};

它用于检查给定类型是否具有名为iterator_category的成员。如果答案是no,则has_iterator_category_convertible_to将简单地默认为false_type。如果不这样做,将访问不存在的名称,并触发硬编译器错误,这不是您在SFINAE中想要做的。