Search code examples
c++visual-c++c++17iterator-traits

Fixing warning with deprecated inheritance of std::iterator since C++17


With C++17 inheriting from std::iterator is deprecated in favour of using iterator traits directly as per for example https://www.fluentcpp.com/2018/05/08/std-iterator-deprecated/

This should be as simple as providing 'using's for each of:

  • iterator_category
  • value_type
  • difference_type
  • pointer
  • reference

and removing the base class. We've just migrated some legacy code to compile under C++23 (under MSVC on windows) and it would be nice to remove warnings as well. As far as I can tell it already defines the correct operations and does rely on inheriting anything like say operator++ from the base class. However this causes cryptic failures such as:

error C2672: 'std::distance': no matching overloaded function found

I notice that even adding a single type definition for one of the above is enough to break something. Even if I define the traits such that they ought to exactly match the definitions provided via std::iterator. Can anyone explain this?

For example the traits should match the base class if you go for an intermediate step:

    template <typename Internal>
    class Iterator: public std::iterator<std::forward_iterator_tag, typename std::iterator_traits<Internal>::value_type>
    {
        using IterType = typename std::iterator<std::forward_iterator_tag, typename std::iterator_traits<Internal>::value_type>;
        using iterator_category = typename IterType::iterator_category;
        using value_type = typename IterType::value_type;
        using difference_type = typename IterType::difference_type;
        using pointer = typename IterType::pointer;
        using reference = typename IterType::reference;

Even just adding using difference_type alone breaks code. I tried a static_assert to see how difference_type was defined and it isn't.

        std::iterator_traits<Internal>::difference_type>, 
              "difference_type mismatch");

But defining it to exactly the same type seems to trigger some dark template voodoo. If I invert the static assertion it fails proving I've defined it correctly.

   static_assert(!std::is_same_v<difference_type, typename 
        std::iterator_traits<Internal>::difference_type>, "difference_type mismatch");

error C2338: static_assert failed: 'difference_type mismatch'

Q What on Earth is going on here?

Note as a workaround we can define _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING but it would be nice to deal with the problem properly. For my own education I would like to understand it as well. Unfortunately I cannot post the entire legacy code in question.

Though its a good idea I cannot presently look at the errors in another compiler to get a different perspective (making that possible is on the todo list but quite far down).

References:


Solution

  • Your typedefs are in private section,

    so typename Iterator<SomeType>::value_type is ill formed at global and std scopes.

    As, std::iterator_traits is SFINAE friendly, and provides the typedef "automatically" only when all typedefs exist (and are accessible).

    So even adding one private expected typedef in non-public section, make std::iterator_traits empty for that