Skip to content

<ranges>: ranges::to use range-based for loops in append branches #5172

Closed
@hewillk

Description

@hewillk

for (auto&& _Elem : _Range) {
using _ElemTy = decltype(_Elem);
if constexpr (_Can_emplace_back<_Container, _ElemTy>) {
_Cont.emplace_back(_STD forward<_ElemTy>(_Elem));
} else if constexpr (_Can_push_back<_Container, _ElemTy>) {
_Cont.push_back(_STD forward<_ElemTy>(_Elem));
} else if constexpr (_Can_emplace_end<_Container, _ElemTy>) {
_Cont.emplace(_Cont.end(), _STD forward<_ElemTy>(_Elem));
} else {
_STL_INTERNAL_STATIC_ASSERT(_Can_insert_end<_Container, _ElemTy>);
_Cont.insert(_Cont.end(), _STD forward<_ElemTy>(_Elem));
}
}

To save the include overhead of ranges::for_each, MSVC-STL uses range-based for loop to iterate over the range. However, this is not guaranteed to be well-formed as the former does not extract iterators and sentinels through ranges::begin/ranges::end.
A disgusting and contrived case could be:

https://godbolt.org/z/Kc3dsvnfW

#include <ranges>

struct Vector {
  void push_back(int);
};

struct OnlyADLRange {
  void begin() = delete;
  void end() = delete;
  friend int* begin(OnlyADLRange&);
  friend int* end(OnlyADLRange&);
};

int main() {
  std::ranges::contiguous_range auto r = OnlyADLRange{};
  auto v = r | std::ranges::to<Vector>(); // only well-formed in libstdc++
}

Above, only libstdc++ explicitly uses ranges::begin/ranges::end to get iterators-pair and iterates over the ranges via a while loop so there is no issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingfixedSomething works now, yay!rangesC++20/23 ranges

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions