C ++: SFINAE for differentiating fill and range constructors?

Here's a question about how to distinguish between fill and range constructors. The code is copied 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;
    }
};

The code above does not work as described in this question. The solution uses SFINAE to define a range constructor, as this 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;
}

This meaning of this solution compares the dereferenced type InputIteratorwith T &. If it is indeed an iterator that points to T, the comparison std::is_samewill be true and a range constructor will be selected; if it is not an iterator, the template replacement will fail, so the range constructor will be deleted, and therefore the fill constructor will be selected.

. InputIterator const_iterator (, cbegin()), const T &, const -ness std::remove_const ( ), std::is_same , .

GNU ++ STL ( ,) - . , const_iterator.

(1) , std::is_same OR, T &, const T &?

(2), , (1), , std::remove_const , , const -ness (const const) , const T &, T &.

+4
2

, 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);

+7

. , , iterator_traits. , :

template<
    typename InputIterator, 
    typename = typename std::iterator_traits<InputIterator>::value_type
>
NaiveVector(InputIterator first, InputIterator last);

, gcc ++ 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()); }

++ 98, , , , 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 an integral type.  If so, it not an iterator.
      typedef typename std::__is_integer<_InputIterator>::__type _Integral;
      _M_initialize_dispatch(__first, __last, _Integral());
    }
+2

Source: https://habr.com/ru/post/1684501/


All Articles