Fix iterator_traits: make SFINAE-friendly for iterators without nested typedefs (#1447)

The primary etl::iterator_traits template previously required all five
nested typedefs (iterator_category, value_type, difference_type,
pointer, reference) to exist, causing hard compilation errors with
iterators like std::common_iterator or ranges::common_iterator.

Changes:

1. Make primary template SFINAE-friendly: split into an empty primary
   template and a void_t-guarded partial specialization that only
   activates when all nested typedefs are present.

2. Add explicit std::common_iterator specialization (C++20/STL): a
   partial specialization that delegates to
   std::iterator_traits<std::common_iterator<I,S>>.

3. Fall through to std::iterator_traits: the empty primary template
   inherits from std::iterator_traits<TIterator> when building with
   STL and C++20, so any iterator with a std::iterator_traits
   specialization works automatically.

4. Remove iterator_traits dependency from etl::vector: assign() now
   uses decltype(*first) for the type-compatibility static assert,
   and insert() uses ptrdiff_t directly instead of querying
   iterator_traits<TIterator>::difference_type.
This commit is contained in:
Roland Reichwein 2026-05-26 10:11:24 +02:00 committed by GitHub
parent 9765cbf764
commit af3944acdb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 65 additions and 6 deletions

View File

@ -68,9 +68,22 @@ namespace etl
//***************************************************************************
// iterator_traits
// For anything not a fundamental type.
template <typename TIterator, typename = typename etl::enable_if< !etl::is_fundamental<TIterator>::value, void>::type>
#if ETL_USING_CPP11
// Primary template: falls through to std::iterator_traits when STL is available (C++20+),
// otherwise empty (SFINAE-safe fallback for iterators without nested typedefs).
template <typename TIterator, typename = void>
struct iterator_traits
#if ETL_USING_STL && ETL_USING_CPP20
: std::iterator_traits<TIterator>
#endif
{
};
// Specialization for iterators that define the standard nested typedefs.
template <typename TIterator>
struct iterator_traits<TIterator, etl::void_t< typename TIterator::iterator_category, typename TIterator::value_type,
typename TIterator::difference_type, typename TIterator::pointer, typename TIterator::reference >>
{
typedef typename TIterator::iterator_category iterator_category;
typedef typename TIterator::value_type value_type;
@ -79,6 +92,14 @@ namespace etl
typedef typename TIterator::reference reference;
};
#if ETL_USING_STL && ETL_USING_CPP20
// Specialization for std::common_iterator (C++20).
template <typename I, typename S>
struct iterator_traits<std::common_iterator<I, S>, void> : std::iterator_traits<std::common_iterator<I, S>>
{
};
#endif
// For pointers.
template <typename T>
struct iterator_traits<T*, void>
@ -101,6 +122,43 @@ namespace etl
typedef const T& reference;
};
#else // C++03
// Primary template: unconditionally extracts nested typedefs.
template <typename TIterator>
struct iterator_traits
{
typedef typename TIterator::iterator_category iterator_category;
typedef typename TIterator::value_type value_type;
typedef typename TIterator::difference_type difference_type;
typedef typename TIterator::pointer pointer;
typedef typename TIterator::reference reference;
};
// For pointers.
template <typename T>
struct iterator_traits<T*>
{
typedef ETL_OR_STD::random_access_iterator_tag iterator_category;
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef typename etl::remove_cv<T>::type* pointer;
typedef T& reference;
};
// For const pointers.
template <typename T>
struct iterator_traits<const T*>
{
typedef ETL_OR_STD::random_access_iterator_tag iterator_category;
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef const typename etl::remove_cv<T>::type* pointer;
typedef const T& reference;
};
#endif
//***************************************************************************
// advance
template <typename TIterator, typename TDistance>

View File

@ -390,9 +390,11 @@ namespace etl
template <typename TIterator>
typename etl::enable_if<!etl::is_integral<TIterator>::value, void>::type assign(TIterator first, TIterator last)
{
#if ETL_USING_CPP11
ETL_STATIC_ASSERT((etl::is_same<typename etl::remove_cv<T>::type,
typename etl::remove_cv< typename etl::iterator_traits< TIterator>::value_type>::type>::value),
typename etl::remove_cv<typename etl::remove_reference<decltype(*first)>::type>::type>::value),
"Iterator type does not match container type");
#endif
#if ETL_IS_DEBUG_BUILD
difference_type d = etl::distance(first, last);
@ -895,12 +897,11 @@ namespace etl
etl::move_backward(p_buffer + insert_begin, p_buffer + insert_begin + copy_old_n, p_buffer + insert_end + copy_old_n);
// Copy construct new.
typedef typename etl::iterator_traits<TIterator>::difference_type diff_t;
etl::uninitialized_copy(first + static_cast<diff_t>(copy_new_n), first + static_cast<diff_t>(copy_new_n + construct_new_n), p_end);
etl::uninitialized_copy(first + static_cast<ptrdiff_t>(copy_new_n), first + static_cast<ptrdiff_t>(copy_new_n + construct_new_n), p_end);
ETL_ADD_DEBUG_COUNT(construct_new_n);
// Copy new.
etl::copy(first, first + static_cast<diff_t>(copy_new_n), p_buffer + insert_begin);
etl::copy(first, first + static_cast<ptrdiff_t>(copy_new_n), p_buffer + insert_begin);
p_end += count;
}