c++ std::views::keys是否保证可以正确处理任何pair/tuple类型?

db2dz4w8  于 9个月前  发布在  其他
关注(0)|答案(3)|浏览(85)

代码粘贴在这里& https://www.godbolt.org/z/qszqYsT4o
我试图提供与c++20视图兼容的boost::adaptors::indexed功能。我的主要用例是std::vector,但我肯定更喜欢一个通用的解决方案。我震惊地发现std::views::keys没有像预期的那样正确地提取第一个元素。
对于GCC 10.3,rng1的输出是垃圾,rng2是预期的。GCC 10.4+工作正常。最新版本的clang无法编译代码。
问题:

  1. std::views::keys是否保证支持任何pair/tuple类型,或者我的代码是UB?
    1.如果clang 14.0.0无法编译,那么我的代码是法律的C吗?
    1.在c
    20中有没有更好的方法来实现这个功能?我看了一下std::span,但似乎不能让它自然地工作。注意:如果可以的话,我很乐意将std::views::zipstd::views::iota一起使用。
#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;
}

字符串

z9ju0rcb

z9ju0rcb1#

std::views::keys是否保证支持任何pair/tuple类型,或者我的代码是UB?
views::keys保证在C20中与pair/tuple一起工作,并且由于P2165,保证在C23中与 * tuple-like * 对象一起工作。
如果clang 14.0.0无法编译,那么我的代码是法律的C吗?
你的代码格式良好。
但是,不一定要使用reference_wrapper,简单的pair<size_t, T&>应该就足够了,也不一定要使用std::views::all(vec)vec | views::transform(fn)会自动将vec转换为view
在C
20中有没有更好的方法来实现这个功能?
在C20中,恐怕不行。在C23中,你可以使用views::enumerate

const std::vector<int> v{10, 20, 30};
for (const auto& [index, value] : v | views::enumerate)
  std::cout << index << " " << value << "\n";

字符串

7rfyedvj

7rfyedvj2#

这是LWG 3502。由于这个特定的问题,您的代码在gcc 10.3上失败,但它已经得到解决,您的代码在gcc 10.4(或11.1等)上工作正常。
问题中的例子应该看起来很熟悉:

std::vector<int> vec = {42};
auto r = vec | std::views::transform([](auto c) { return std::make_tuple(c, c); })
             | std::views::keys;

字符串
它失败,因为elements_viewoperator*被指定为:

constexpr decltype(auto) operator*() const { return get<N>(*current_); }


当第N个元素是纯右值时(就像在那个例子和你的例子中一样),这是一个直接悬挂的右值引用。这个问题的解决方案确保了operator*在这些情况下返回纯右值,所以一切正常。
请注意,没有理由写:

return std::views::all(vec) | std::views::transform(fn);


范围适配器已经为你做了。你可以写:

return vec | std::views::transform(fn);

mdfafbf1

mdfafbf13#

当然,修改底层序列的结构(例如调用std::vector::push_back)是未定义的行为。

template <typename T, std::size_t Extent>
auto make_indexed_view(const std::span<T, Extent>& span)
{
    auto fn = [span](T& val) {
        return IndexValuePair<T>{static_cast<std::size_t>(&val - span.data()), val};
    };
    return span | std::views::transform(fn);    
}

int main()
{
    constexpr int data[] = {10, 20, 30};
    auto fn = [](const auto& elem) { return elem.second >= 20; };
    auto rng = make_indexed_view(std::span{data}) | std::views::filter(fn) | std::views::transform(get_first);
    for (auto&& elem : rng) {
        std::cout << elem << '\n';
    }
    return 0;
}

字符串

相关问题