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

时间:2014-06-21 23:46:26

标签: c++ vector constructor

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

它似乎运行良好,除非我创建一个Vec对象试图使用
 Vec(size_type n,const T& val = T())构造函数,它反而想要使用模板化的构造函数,它应该带两个迭代器/指针,并给我错误。

我相信我理解为什么会发生这种情况,但是如果不改变使用类的方式,我就无法想到一种方法可以减少它的模糊性。我想保持使用与std :: vector相同。

如何才能获得正确的构造函数? std :: vector如何避免这个问题?

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);
}

我收到错误:

||=== Build: Debug in Vec Class (compiler: GNU GCC Compiler) ===|
c:\program files\codeblocks\mingw\bin\..\lib\gcc\mingw32\4.7.1\include\c++\bits\stl_uninitialized.h||In instantiation of '_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = int; _ForwardIterator = int*]':|
F:\CBProjects\Accelerated C++\Programming with Classes\Vec Class\Vec.h|58|required from 'void Vec::create(InputIt, InputIt) [with InputIt = int; T = int]'|
F:\CBProjects\Accelerated C++\Programming with Classes\Vec Class\Vec.h|24|required from 'Vec::Vec(InputIt, InputIt) [with InputIt = int; T = int]'|
F:\CBProjects\Accelerated C++\Programming with Classes\Vec Class\main.cpp|9|required from here|
c:\program files\codeblocks\mingw\bin\..\lib\gcc\mingw32\4.7.1\include\c++\bits\stl_uninitialized.h|113|error: no type named 'value_type' in 'struct std::iterator_traits'|
||=== Build failed: 1 error(s), 4 warning(s) (0 minute(s), 0 second(s)) ===|

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

修改

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

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

它似乎工作正常,但我实际上对enable_if不太熟悉,所以如果有人知道这种方法有任何危险,请告诉我。

1 个答案:

答案 0 :(得分:2)

选择模板化构造函数,因为它更适合参数。

查看vector constructor的文档,您将看到一条注释,如果参数满足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);

我删除了不必要的部分,以进一步区分InputIteratorForwardIteratorSFINAE is the 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> {};

它的本质是它检查给定类型是否具有可以转换为iterator_category的{​​{1}}标记。

input_iterator_tag更加模糊:

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。如果不这样做,将访问一个不存在的名称,并且将触发硬编译器错误,这不是您想要在SFINAE中执行的操作。