<ranges>
alias templates
An alias template is an alias for another type, which can make code more readable. For example, the following alias, conditional_t
, is an alias for either borrowed_range
or dangling
range, depending on the kind of range
that's passed in:
// requires /std:c++20, or later
#include <iostream>
#include <list>
#include <span>
#include <algorithm>
#include <ranges>
#include <type_traits>
using namespace std;
// Define an alias template called my_iterator_t
// If the provided range R is a borrowed_range, then the
// returned type is iterator_t<R>; otherwise, ranges::dangling
template<ranges::range R>
using my_iterator_t = conditional_t<
ranges::borrowed_range<R>,
ranges::iterator_t<R>, ranges::dangling>;
int main()
{
my_iterator_t<list<int>> aDanglingRange; // list<> isn't a borrowed_range
constexpr bool same = same_as<
decltype(aDanglingRange),
ranges::dangling>; // true
my_iterator_t<span<int, 5>> anIterator_t; // span<> is a borrowed_range
constexpr bool same2 = same_as<
decltype(anIterator_t),
ranges::iterator_t<span<int, 5>>>; // true
cout << boolalpha << same << "," << same2; // outputs true, true
}
For more information about alias templates, see Aliases and typedefs.
The <algorithm>
header defines the following alias templates that determine the types of iterators and sentinels for a range
:
Alias template | Description |
---|---|
borrowed_iterator_t C++20 |
Determine if an iterator returned for a range refers to a range whose lifetime has ended. |
borrowed_subrange_t C++20 |
Determine if a subrange returned for a range refers to a range whose lifetime has ended. |
dangling C++20 |
Indicates that the returned iterator of a range /subrange outlives the lifetime of the range /subrange it refers to. |
iterator_t C++20 |
Returns the iterator type for the specified range. |
range_difference_t C++20 |
Returns the difference type for the specified range's iterator. |
range_reference_t C++20 |
Returns the reference type for the specified range's iterator. |
range_rvalue_reference_t C++20 |
Returns the rvalue reference type for the specified range's iterator. In other words, the rvalue reference type of the range's elements. |
range_size_t C++20 |
Returns the type used to report the specified range's size . |
range_value_t C++20 |
Returns the value type of specified range's iterator. Or in other words, the type of the elements in the range. |
sentinel_t C++20 |
Returns the sentinel type for the specified range. |
borrowed_iterator_t
When an algorithm function that returns an iterator is called with an rvalue range
argument, the range's lifetime could end following the call. That means the returned iterator could refer to elements whose lifetimes have ended. Using a dangling iterator results in undefined behavior.
This template alias returns ranges::dangling
to indicate that this is the situation for the given range argument, or std::ranges::iterator_t<R>
to indicate that it's safe to use the returned iterator because the range it refers to models borrowed_range
or the range was passed as an lvalue.
template<ranges::range R>
using borrowed_iterator_t = conditional_t<ranges::borrowed_range<R>,
ranges::iterator_t<R>, ranges::dangling>;
Parameters
R
The range to test.
Remarks
The lifetime of an rvalue range can end following a function call whether the range models borrowed_range
or not. If it's a borrowed_range
, you may be able to continue to use the iterators with well-defined behavior regardless of when the range's lifetime ends.
Cases where this isn't true are, for example, for containers like vector
or list
because when the container's lifetime ends, the iterators would refer to elements that have been destroyed.
You can continue to use the iterators for a borrowed_range
, for example, for a view
like iota_view<int>{0, 42}
whose iterators are over set of values that aren't subject to being destroyed because they're generated on demand.
If an algorithm function is passed a range whose iterators depend on its lifetime, ranges::dangling
is returned instead of an iterator or subrange, so potential misuse is detected at compile time.
Example: borrowed_iterator_t
The following example shows how borrowed_iterator_t
detects a dangling iterator. The function ranges::max_element()
uses this template alias to determine the return type:
// requires /std:c++20, or later
#include <vector>
#include <span>
#include <ranges>
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
// Not dangling ------------------
int a[] = {0,1,2,3};
// not dangling even though an rvalue because span models ranges::borrowed
auto result1 = ranges::max_element(span{a});
cout << boolalpha << ranges::borrowed_range<decltype(span{a})> << endl; // outputs true because the temporary models ranges::borrowed
cout << same_as<decltype(result1), ranges::dangling> << endl; // outputs false because the result isn't dangling
vector<int> v{0,1,2,3}; // doesn't model ranges::borrowed
auto result2 = ranges::max_element(v); // Yet not dangling because passed as an lvalue
cout << same_as<decltype(result2), ranges::dangling> << endl; // outputs false because the result isn't dangling
// Dangling ------------------
auto result3 = ranges::max_element(vector{0,1,2,3}); // dangling because vector doesn't model ranges::borrowed and is passed as an rvalue
cout << same_as<decltype(result3), ranges::dangling>; // outputs true because the result is dangling
}
true
false
false
true
borrowed_subrange_t
When an algorithm function that returns a subrange
is called with an rvalue range
argument, the range's lifetime could end following the call. That means the returned subrange
could refer to elements whose lifetimes have ended. Using a dangling subrange
results in undefined behavior.
This template alias either returns ranges::dangling
to indicate that this could be the situation for the given range argument, or subrange<ranges::iterator_t<R>>
to indicate that it's safe to use the returned subrange because either the range whose elements it refers to models borrowed_range
or the range was passed as an lvalue.
template<ranges::range R>
using borrowed_subrange_t = conditional_t<ranges::borrowed_range<R>,
ranges::subrange<ranges::iterator_t<R>>, ranges::dangling>;
Parameters
R
The range to test.
Remarks
The lifetime of an rvalue range can end following a function call whether the range models borrowed_range
or not. If it's a borrowed_range
, you may be able to continue to use the iterators with well-defined behavior regardless of when the range's lifetime ends.
Cases where this isn't true are, for example, for containers like vector
or list
because when the container's lifetime ends, the iterators would refer to elements that have been destroyed.
You can continue to use the iterators for a borrowed_range
, for example, for a view
like iota_view<int>{0, 42}
whose iterators are over set of values that aren't subject to being destroyed because they're generated on demand.
If an algorithm function is passed a range whose iterators depend on its lifetime, ranges::dangling
is returned instead of a subrange so that potential misuse is detected at compile time.
Example: borrowed_subrange_t
The following example shows how borrowed_subrange_t
detects a dangling iterator because equal_range()
and max_element
use this template alias to determine the return type:
// requires /std:c++20, or later
#include <vector>
#include <iostream>
#include <algorithm>
#include <span>
#include <ranges>
int main()
{
using namespace std;
// Not dangling ------------------
vector vec{0, 1, 1, 2};
auto result1 = ranges::equal_range(span{vec}, 1); // not dangling even though passing as an rvalue because span models borrowed_range
cout << boolalpha << ranges::borrowed_range<decltype(span{vec})> << endl; // true because the temporary is a borrowed range
cout << boolalpha << same_as<decltype(result1), ranges::dangling> << endl; // false because the result isn't dangling
// result2 isn't dangling even though vec doesn't model ranges::borrowed because it's an lvalue
auto result2 = ranges::max_element(vec);
cout << boolalpha << ranges::borrowed_range<decltype(vec)> << endl; // false because vector isn't a borrowed_range
cout << boolalpha << same_as<decltype(result2), ranges::dangling> << endl; // false because the result isn't dangling
// Dangling -----------------------
// result3 is dangling because the temporary is an rvalue that doesn't model borrowed_range
auto result3 = ranges::max_element(vector{0,1,1,2});
cout << boolalpha << same_as<decltype(result3), ranges::dangling> << endl; // true because the result is dangling
}
true
false
false
false
true
dangling
If an algorithm function that returns an iterator or a subrange
is called with an rvalue range
argument, the range argument's lifetime could end following the call. That means the returned iterator or subrange
could refer to elements whose lifetimes have ended. Using a dangling iterator or subrange
results in undefined behavior.
If an algorithm function is passed a range whose iterators depend on its lifetime, ranges::dangling
is returned instead of an iterator or subrange so that potential misuse is detected at compile time.
1) constexpr dangling() noexcept = default;
2) template<class... Args>
constexpr dangling(Args&&...) noexcept {}
Parameters
Args
A variable number of non-void
types. They have no effect. The arguments are a convenience so that you don't need different code paths to handle constructing the iterator type versus the dangling
type. This is useful when the passed in value indicates that dangling
should be returned instead of an iterator.
Example: dangling
The following example shows how max_element
detects a dangling iterator.
// requires /std:c++20, or later
#include <vector>
#include <iostream>
#include <ranges>
#include <algorithm>
using namespace std;
int main()
{
auto result1 = ranges::max_element(vector{1,2,3}); // dangling because vector doesn't model ranges::borrowed and is passed as an rvalue
cout << boolalpha << same_as<decltype(result1), ranges::dangling> << endl; // outputs true because the result is dangling
vector<int> v{3,4,5};
auto result2 = ranges::max_element(v); // Not dangling because passed as an lvalue
cout << same_as<decltype(result2), ranges::dangling>; // outputs false because the result isn't dangling
}
true
false
iterator_t
This template alias returns the iterator type used to iterate over the provided range type.
template<class T>
using iterator_t = decltype(ranges::begin(declval<T&>()));
Parameters
T
The range type to get the iterator type for.
Example: iterator_t
The following example shows how iterator_t
can be used to declare an iterator for a vector:
// requires /std:c++20, or later
#include <vector>
#include <ranges>
#include <iostream>
int main()
{
using namespace std;
vector<int> v{1,2,3};
ranges::iterator_t<decltype(v)> it = v.begin();
cout << *it << "\n"; // outputs 1
cout << typeid(it).name(); // outputs class _Vector_iterator<class _Vector_val<struct _Simple_types<int>>>
}
1
class std::_Vector_iterator<class std::_Vector_val<struct std::_Simple_types<int> > >
range_difference_t
Returns the difference type for the specified range's iterator.
template<range R>
using range_difference_t = iter_difference_t<iterator_t<R>>;
Parameters
R
The range whose iterator will provide the difference type.
Example: range_difference_t
The following example shows how range_difference_t
is used to hold the distance between elements in a range:
// requires /std:c++20, or later
#include <vector>
#include <ranges>
#include <iostream>
int main()
{
using namespace std;
vector<int> v{1,2,3};
auto findIt = ranges::find(v, 2);
// type of distance is ptrdiff_t
ranges::range_difference_t<decltype(v)> distance = ranges::distance(v.begin(), findIt);
cout << distance << endl; // outputs 1
}
1
range_reference_t
Returns the reference type for the specified range's iterator. In other words, the reference type of the range's elements.
template <range R>
using range_reference_t = iter_reference_t<ranges::iterator_t<R>>;
Parameters
R
The range for which the reference type of its iterator type is returned.
Example: range_reference_t
The following example shows range_reference_t
referring to the type of the elements in a range:
// requires /std:c++20, or later
#include <vector>
#include <ranges>
#include <iostream>
int main()
{
using namespace std;
vector<int> v{1,2,3};
ranges::range_reference_t<decltype(v)> ref = v[0];
cout << ref << endl; // outputs 1
cout << typeid(ref).name() << endl; // outputs int
}
1
int
range_rvalue_reference_t
Returns the rvalue reference type for the specified range's iterator. In other words, the rvalue reference type of the range's elements.
template <range R>
using range_rvalue_reference_t = iter_reference_t<ranges::iterator_t<R>>;
Parameters
R
The range to get the rvalue reference type to its iterator type.
Example: range_rvalue_reference_t
The following example shows range_rvalue_reference_t
referring to a rvalue type of the elements in a range:
// requires /std:c++20, or later
#include <vector>
#include <ranges>
#include <iostream>
int main()
{
using namespace std;
vector<int> v{1,2,3};
ranges::range_rvalue_reference_t<decltype(v)> elementRvalueType = v[0] * 10; // elementRvalueType is int&&
cout << elementRvalueType << endl; // outputs 10
cout << typeid(elementRvalueType).name() << endl; // outputs int
}
10
int
range_size_t
Returns the type of the size
function for the specified sized_range
.
template <range R>
using range_size_t = iter_reference_t<ranges::iterator_t<R>>;
Parameters
R
The range to get the type of its size
function.
Example: range_size_t
The following example shows range_size_t
referring to the number of elements in a range:
// requires /std:c++20, or later
#include <vector>
#include <iostream>
#include <ranges>
int main()
{
using namespace std;
vector<int> v{1,2,3};
ranges::range_size_t<decltype(v)> size = v.size();
cout << size << endl; // outputs 3
cout << typeid(size).name(); // outputs unsigned __int64
}
3
unsigned __int64
range_value_t
Returns the value type of specified range's iterator. Or in other words, the type of the elements in the range.
template <ranges::range R>
using range_value_t = iter_value_t<ranges::iterator_t<R>>;
Parameters
R
The range to get the value type of its iterator.
Example: range_value_t
The following example shows how range_value_t
refers to the type of elements in a range:
// requires /std:c++20, or later
#include <vector>
#include <ranges>
#include <iostream>
int main()
{
using namespace std;
vector<int> v{1,2,3};
ranges::range_value_t<decltype(v)> elementType = v[2]; // elementType is an int
cout << elementType << endl; // outputs 3
cout << typeid(elementType).name() << endl; // outputs int
}
3
unsigned int
sentinel_t
Returns the sentinel type for the specified range.
template <range R>
using sentinel_t = decltype(ranges::end(declval<R&>()));
Parameters
R
The range to get the sentinel type for.
Example: sentinel_t
The following example shows using sentinel_t
to determine whether the iterator type and sentinel type are the same:
// requires /std:c++20, or later
#include <iostream>
#include <list>
#include <ranges>
int main()
{
using namespace std;
list myList{1, 2, 3};
ranges::subrange count = std::views::counted(myList.begin(), myList.size());
ranges::iterator_t<decltype(count)> first;
ranges::sentinel_t<decltype(count)> last;
// The iterator type and the sentinel type of a subrange
// obtained from views::counted are not the same
cout << boolalpha << is_same<decltype(first), decltype(last)>::value << endl; // outputs false
cout << "iter: " << typeid(first).name() << "\n\n end: " << typeid(last).name() << endl;
}
false
iter: class std::counted_iterator<class std::_List_iterator<class std::_List_val<struct std::_List_simple_types<int> > > >
end: struct std::default_sentinel_t