This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.

4402. List-initialization of iterators in [simd.mask.overview]

Section: 29.10.9.1 [simd.mask.overview] Status: New Submitter: Arthur O'Dwyer Opened: 2025-10-02 Last modified: 2025-10-04

Priority: Not Prioritized

View all issues with New status.

Discussion:

29.10.9.1 [simd.mask.overview] has

namespace std::simd {
 template<class V>
 class simd-iterator { // exposition only
  V* data_ = nullptr; // exposition only
  simd-size-type offset_ = 0; // exposition only
  constexpr simd-iterator(V& d, simd-size-type off) noexcept; // exposition only
  […]
 };
[…]
 template<size_t Bytes, class Abi>
  class basic_mask {
  public:
   using value_type = bool;
   using abi_type = Abi;
   using iterator = simd-iterator<basic_mask>;
   using const_iterator = simd-iterator<const basic_mask>;
   constexpr iterator begin() noexcept { return {*this, 0}; }
   constexpr const_iterator begin() const noexcept { return {*this, 0}; }
   constexpr const_iterator cbegin() const noexcept { return {*this, 0}; }
   constexpr default_sentinel_t end() const noexcept { return {}; }
   constexpr default_sentinel_t cend() const noexcept { return {}; }
   […]

It's unclear whether the "exposition-only" constructor is required to be present. If it is present, as written, without explicit, then {someBasicMask, 0} becomes a valid initializer for an iterator. Evidence in favor of its intentionality: the use of return {*this, 0} in basic_mask::begin(). (The constructor is private, but it still participates in overload resolution and will ambiguate other possible conversions.) But this makes many expressions ambiguous that could be unambiguous to a human.

using Mask = std::simd::mask<int>;

void overloaded(std::string, std::pair<Mask, int> kv);
void overloaded(std::string, Mask::iterator it);

int main() {
  Mask m;
  overloaded("the pair is", {m, 0});  // ambiguous?
}

At the very least, we should say that this list-initialization is intentional, and add wording to class simd-iterator and/or remove the "exposition only" from simd-iterator's constructor. That makes it clear that the above program is indeed intended to be ambiguous. But IMO we should instead simply make the above program valid.

Proposed resolution:

This wording is relative to N5014.

  1. Modify 29.10.6 [simd.iterator] as indicated:

    namespace std::simd {
      template<class V>
      class simd-iterator {                                                  // exposition only
        V* data_ = nullptr;                                                  // exposition only
        simd-size-type offset_ = 0;                                          // exposition only
        constexpr explicit simd-iterator(V& d, simd-size-type off) noexcept; // exposition only
        
        […]
      };
    }
    
  2. Modify 29.10.7.1 [simd.overview] as indicated:

    namespace std::simd {
      template<size_t T, class Abi> class basic_vec {
      public:
        using value_type = T;
        using mask_type = basic_mask<sizeof(T), Abi>;
        using abi_type = Abi;
        using iterator = simd-iterator<basic_vec>;
        using const_iterator = simd-iterator<const basic_vec>;
    
        constexpr iterator begin() noexcept { return iterator({*this, 0}); }
        constexpr const_iterator begin() const noexcept { return const_iterator({*this, 0}); }
        constexpr const_iterator cbegin() const noexcept { return const_iterator({*this, 0}); }
        constexpr default_sentinel_t end() const noexcept { return {}; }
        constexpr default_sentinel_t cend() const noexcept { return {}; }
        
        […]
      };
    }
    
  3. Modify 29.10.9.1 [simd.mask.overview] as indicated:

    namespace std::simd {
      template<size_t Bytes, class Abi> class basic_mask {
      public:
        using value_type = bool;
        using abi_type = Abi;
        using iterator = simd-iterator<basic_mask>;
        using const_iterator = simd-iterator<const basic_mask>;
    
        constexpr iterator begin() noexcept { return iterator({*this, 0}); }
        constexpr const_iterator begin() const noexcept { return const_iterator({*this, 0}); }
        constexpr const_iterator cbegin() const noexcept { return const_iterator({*this, 0}); }
        constexpr default_sentinel_t end() const noexcept { return {}; }
        constexpr default_sentinel_t cend() const noexcept { return {}; }
        
        […]
      };
    }