我是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不太熟悉,所以如果有人知道这种方法有任何危险,请告诉我。
答案 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);
我删除了不必要的部分,以进一步区分InputIterator
和ForwardIterator
。 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
的{{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中执行的操作。