我在做什么?
我正在尝试实现标准库vector
。有一个名为assign()
的成员函数重载为:
template<typename InputIterator>
void assign(InputIterator first, InputIterator last)
void assign(size_type n, const value_type& val)
void assign(std::initializer_list<value_type> il)
我的实现:
template<typename InputIterator>
void assign(InputIterator first, InputIterator last)
{
difference_type t_ptrdiff = last - first;
if(t_ptrdiff > m_size) reallocate(size_type(t_ptrdiff) << 1);
for(size_type idx = 0; first != last; ++idx, ++first)
m_base[idx] = *first;
m_size = t_ptrdiff;
}
void assign(size_type n, const value_type& val)
{
if(n > m_size) reallocate(n << 1);
for(size_type idx = 0; idx != n; ++idx)
m_base[idx] = val;
m_size = n;
}
void assign(std::initializer_list<value_type> il)
{
typename std::initializer_list<value_type>::size_type il_size = il.size();
typename std::initializer_list<value_type>::iterator end = il.end();
if(il_size > m_size) reallocate(il_size << 1);
size_type idx = 0;
for(typename std::initializer_list<value_type>::iterator begin = il.begin(); begin != end; ++idx, ++begin)
m_base[idx] = *begin;
m_size = il_size;
}
另外,我很确定这是重新分配堆内存的糟糕方法,但这是我目前拥有的:
void reallocate(size_type requested_capacity)
{
value_type* t_base = new value_type[requested_capacity];
m_capacity = requested_capacity;
if(m_base == nullptr){
m_base = t_base;
return;
}
if(m_capacity < m_size)
m_size = m_capacity;
for(size_type idx = 0; idx < m_size; ++idx)
t_base[idx] = m_base[idx];
delete [] m_base;
m_base = t_base;
}
问题:
当我使用整型对象调用这些方法时,一切都按预期工作,例如:
vector<int> v;
// This will correctly call: void assign(size_type n, const value_type& val)
vector<int>::size_type n = 7;
vector<int>::value_type val = 100;
v.assign (n, val);
// This will correctly call: void assign(InputIterator first, InputIterator last)
vector<int>::iterator it = v.begin() + 1;
v.assign (it, v.end()-1);
// This will correctly call: void assign(std::initializer_list<value_type> il)
v.assign ({1776,7,4});
但是当我尝试使用整数文本调用assign()
时,例如:v.assign(7, 100)
,编译器抱怨以下错误,因为它与模板化函数匹配:void assign(InputIterator first, InputIterator last)
:
In file included from main.cpp:LINE:
vector.h: In instantiation of ‘void vector<T>::assign(InputIterator, InputIterator) [with InputIterator = int; T = int]’:
main.cpp:LINE:COLUMN: required from here
vector.h:LINE:COLUMN: error: invalid type argument of unary ‘*’ (have ‘int’)
xxx | m_base[idx] = *first;
libstdc++
实现:
我检查了libstdc++
实现,这是它们的函数声明:
void assign(size_type __n, const value_type& __val);
template<typename _InputIterator>
void assign(_InputIterator __first, _InputIterator __last);
void assign(initializer_list<value_type> __l);
与标准库的std::vector
没有这样的冲突;v.assign(7, 100)
正确解析为void assign(size_type __n, const value_type& __val)
;但不在我的实现中。
更新:
我最近了解了std::enable_if
和SFINAE,这一切都归功于n.1.8e9-where's-my-share m.在下面的答案。我是这样做的:
template<typename InputIterator>
typename std::enable_if<std::is_pointer<InputIterator>::value>::type
assign(InputIterator first, InputIterator last);
它使用
std::is_pointer
检查InputIterator
是否真的是一个指针;但是,(如果我错了,请纠正我)并非所有迭代器都需要是指针,它们只需要遵守某些规则(包括取消引用、递增、相等可比)。这是我能做的最好的事情,因为即使n. 1.8e9-where's-my-share m.提供了一个示例,我仍然无法找出正确的语法。
此外,我最终使用
std::enable_if<>::type
作为assign()
函数的类型。这里无关紧要,因为此函数的类型为void
.但是,如果我需要归还一些东西怎么办。那么正确的语法是什么?
我的问题现在已经解决了,但如果有人想提供比我最终所做的更好的实现,请不要犹豫; 将不胜感激!
重载
template <typename InputIterator>
void assign(InputIterator first, InputIterator last)
仅当 InputIterator满足 LegacyInputIterator see 时,才参与重载解析。
对于实现者来说,这意味着在C++20之前需要某种SFINAE。在 C++20 中,您可以改用概念。
要去除非迭代器,您可以做这样的事情
template <typename InputIterator>
std::enable_if_t<???> assign ...
其中???
是一个常量表达式,仅对 InputIterator 有效(并且为 true),它是 LegacyInputIterator。
我不会写下整个条件,但这是其中的一部分。要确保为输入迭代器定义取消引用,您可以编写
sizeof(*std::declval<InputIterator>()) > 0
如果定义了取消引用运算符,则此条件的计算结果为 true,如果未定义,则替换失败。
结合这里的所有条件,你应该被设置。
问题是7
和100
都是int
文字,因此模板化重载比非模板版本更匹配,因为在非模板版本中,第一个参数需要从int
转换为size_type
,而在模板版本中不需要转换,因为InputIterator
被推断为int
。
解决此问题的一种可能方法是在第一个参数7
后添加后缀u
或将第一个参数转换为size_type
。
下面是一个人为的例子:
template<typename InputIterator>
void assign(InputIterator first, InputIterator last) //#1
{
std::cout<<"template version"<<std::endl;
}
void assign(std::size_t n, const int& val) //#2
{
std::cout<<"nontemplate versino"<<std::endl;
}
int main()
{
assign(7,100); //calls #1 as #1 is a better match
assign(7u, 100); //calls #2
// ^------------->added u
return 0;
}