Here是一个询问如何区分填充和范围构造函数的问题。代码复制到这里:
template <typename T>
struct NaiveVector {
vector<T> v;
NaiveVector(size_t num, const T &val) : v(num, val) { // fill
cout << "(int num, const T &val)" << endl;
}
template <typename InputIterator>
NaiveVector(InputIterator first, InputIterator last) : v(first, last) { // range
cout << "(InputIterator first, InputIterator last)" << endl;
}
};
上面的代码不起作用,如该问题所述。解决方案是使用SFINAE来定义范围构造函数,如example:
template
<
typename InputIterator
, typename = typename ::std::enable_if
<
::std::is_same
<
T &
, typename ::std::remove_const
<
decltype(*(::std::declval< InputIterator >()))
>::type
>::value
, void
>::type
>
NaiveVector(InputIterator first, InputIterator last) : v(first, last)
{
cout << "(InputIterator first, InputIterator last)" << endl;
}
此解决方案的要点是将InputIterator
的解除引用类型与T &
进行比较。如果它确实是指向T
的迭代器,则std::is_same
的比较将为真,并且将选择范围构造函数;如果它不是迭代器,则会出现模板替换失败,因此将删除范围构造函数,因此将选择填充构造函数。
但是,上述解决方案存在问题。如果输入InputIterator
属于const_iterator
类型(例如cbegin()
),则取消引用它将产生const T &
,并且其const
- ness无法通过std::remove_const
(解释为here),因此std::is_same
中的比较将为false,从而导致范围构造函数被错误删除。
由于这个原因,GNU的C ++ STL可能有bug(我猜)。在这个bug中,该方法只接受迭代器,但不接受const_iterator。
问题:
(1)是否有一个更好的解决方法,而不是将两个std::is_same
条件与OR运算符组合,一个将解除引用的类型与T &
进行比较,另一个与{{1}进行比较}}?
(2)如果采用(1)中描述的变通方法,const T &
仍然是必需的,现在它不能从引用类型中删除std::remove_const
- 并且取消引用(const或非) -const)迭代器将始终生成引用,const
或const T &
。
答案 0 :(得分:7)
示例代码确实是错误的,因为它不支持const_iterator
,std::decay
会更合适:
template
<
typename InputIterator
, typename = typename ::std::enable_if
<
::std::is_same
<
T
, typename ::std::decay
<
decltype(*(::std::declval< InputIterator >()))
>::type
>::value
, void
>::type
>
NaiveVector(InputIterator first, InputIterator last);
甚至更简单std::iterator_traits<InputIterator>::value_type
。
但更好的是,IMO将是std::is_constructible
。迭代器返回包装器时的句柄(可能是std::vector<bool>
)
template <typename InputIterator,
typename ::std::enable_if<
::std::is_constructible<
T,
decltype(*::std::declval<InputIterator>())
>::value
>::type* = nullptr
>
NaiveVector(InputIterator first, InputIterator last);
答案 1 :(得分:2)
另一个答案是正确的。
为了完成它,我使用iterator_traits
将代码放在这里。
我认为它在概念上要好得多:
template<
typename InputIterator,
typename = typename std::iterator_traits<InputIterator>::value_type
>
NaiveVector(InputIterator first, InputIterator last);
作为参考,这是在gcc C ++ 11库中完成的方法(它检查InputIter概念,它可能更具限制性):
template<typename _InputIterator,
typename = std::_RequireInputIter<_InputIterator>>
vector(_InputIterator __first, _InputIterator __last,
const allocator_type& __a = allocator_type())
: _Base(__a)
{ _M_initialize_dispatch(__first, __last, __false_type()); }
在旧的C ++ 98中,出乎意料地没有做任何特殊的事情,所以可能有调度是在构造函数内完成的, case std::vector<std::size_t>
的模糊情况(我猜!)InputIterator
实际上不是InputIterator。
template<typename _InputIterator>
vector(_InputIterator __first, _InputIterator __last,
const allocator_type& __a = allocator_type())
: _Base(__a)
{
// Check whether it's an integral type. If so, it's not an iterator.
typedef typename std::__is_integer<_InputIterator>::__type _Integral;
_M_initialize_dispatch(__first, __last, _Integral());
}