如何重载相同数量参数的普通函数和模板函数?



我在做什么?

我正在尝试实现标准库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_ifSFINAE,这一切都归功于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,如果未定义,则替换失败

结合这里的所有条件,你应该被设置。

问题是7100都是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;
}

相关内容

  • 没有找到相关文章

最新更新