Code is pasted here & https://www.godbolt.org/z/qszqYsT4o
I am attempting to provide boost::adaptors::indexed functionality that is compatible with c++20 views. My primary use case is for std::vector, but I would definitely prefer a generic solution. I was shocked to discover that std::views::keys did not work as expected to extract the first element correctly.
For GCC 10.3, the output of rng1 is garbage and rng2 is as expected. GCC 10.4+ works fine.
The latest version of clang fails to compile the code.
Questions:
- Is
std::views::keysguaranteed to support any pair/tuple type, or is my code UB? - Given clang 14.0.0 fails to compile, is my code legal C++?
- Is there a better way to achieve this functionality in c++20? I was looking at
std::spanfor a bit, but couldn't seem to make it work naturally. Note: I would have gladly usedstd::views::zipwithstd::views::iotaif it were available.
#include <vector>
#include <ranges>
#include <iostream>
template <typename T>
using IndexValuePair = std::pair<std::size_t, std::reference_wrapper<T>>;
template <typename T, typename Allocator>
auto make_indexed_view(std::vector<T, Allocator>& vec)
{
auto fn = [&vec](T& val) {
return IndexValuePair<T>{static_cast<std::size_t>(&val - vec.data()), val};
};
return std::views::all(vec) | std::views::transform(fn);
}
template <typename T, typename Allocator>
auto make_indexed_view(const std::vector<T, Allocator>& vec)
{
auto fn = [&vec](const T& val) {
return IndexValuePair<const T>{static_cast<std::size_t>(&val - vec.data()), val};
};
return std::views::all(vec) | std::views::transform(fn);
}
struct GetFirstSafely {
template <typename T1, typename T2>
const T1& operator()(const std::pair<T1, T2>& p) const { return std::get<0>(p); }
template <typename T1, typename T2>
T1 operator()(std::pair<T1, T2>&& p) const { return std::get<0>(std::move(p)); }
};
auto get_first = [](auto&& p) -> decltype(auto) { return GetFirstSafely{}(std::forward<decltype(p)>(p)); };
int main()
{
const std::vector<int> v{10, 20, 30};
auto fn = [](const auto& val) { return val.second >= 20; };
auto rng1 = make_indexed_view(v) | std::views::filter(fn) | std::views::keys;
auto rng2 = make_indexed_view(v) | std::views::filter(fn) | std::views::transform(get_first);
for (auto&& elem : rng1)
std::cout << elem << '\n';
for (auto&& elem : rng2)
std::cout << elem << '\n';
return 0;
}