【问题标题】:Wrong constructor called in custom vector class在自定义向量类中调用了错误的构造函数
【发布时间】:2014-08-12 08:50:40
【问题描述】:

我对 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

除了我上面提到的情况外,它似乎工作正常。

ma​​in.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_uninitialized.h||在'_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = int; _ForwardIterator = 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_uninitialized.h|113|错误:'struct std: 中没有名为'value_type'的类型: :iterator_traits'| ||=== 构建失败: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 不是很熟悉,所以如果有人知道这种方法有任何危险,请告诉我。

【问题讨论】:

  • Vec(const Vec&amp; v) { create(v.begin(), v.end(); } 中缺少一个 )
  • @R Sahu:这只是帖子上的一个错字,而不是真正的程序。感谢您指出这一点。
  • 解决方法Vec&lt;int&gt; v1(Vec&lt;int&gt;::size_type(10), 100);
  • @pmr 为什么不起作用?
  • @NeilKirk 谢谢,确实有效。有点笨拙,但解决了问题。

标签: c++ vector constructor


【解决方案1】:

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

查看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);

我删除了进一步区分InputIteratorForwardIterator 的不必要部分。 SFINAE 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标签可以转换为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 的成员。如果答案是nohas_iterator_category_convertible_to 将默认为false_type。如果不这样做,将访问不存在的名称并触发硬编译器错误,这不是您在 SFINAE 中想要做的。

【讨论】:

  • 但是如果传入一个原始指针呢?它不会有标签。还是在 _is_input_iterator 中说明了这一点?
  • @liquidhand iterator_traits 需要专门用于原始指针。
  • 我现在明白为什么大多数简单向量类的例子都忽略了构造函数。这有点超出了大多数初学者课程的范围。感谢您帮助我理解。
  • @liquidhand 是的,它涉及两种更高级的模板技术。但它在展示模板实际上有多复杂方面非常有效。
  • 我想出了一个使用 enable_if 和 !is_arithmetic 的更简单的解决方法。因为据我所知,只有 int 会造成任何构造函数混淆。它可能并不理想,但它似乎正在发挥作用。
猜你喜欢
  • 2012-05-15
  • 1970-01-01
  • 2017-01-28
  • 1970-01-01
  • 2013-05-25
  • 2021-11-06
  • 1970-01-01
  • 2019-05-29
  • 2017-11-15
相关资源
最近更新 更多