首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >std::views::键是否保证在任何对/元组类型下都能正确工作?

std::views::键是否保证在任何对/元组类型下都能正确工作?
EN

Stack Overflow用户
提问于 2022-07-30 17:59:53
回答 3查看 202关注 0票数 2

代码粘贴在这里& https://www.godbolt.org/z/qszqYsT4o

我试图提供与boost::adaptors::indexed视图兼容的c++20功能。我的主要用例是std::vector,但我肯定更喜欢通用解决方案。我震惊地发现,std::views::keys并没有像预期的那样正确地提取第一个元素。

对GCC 10.3来说,rng1的输出是垃圾,而rng2是预期的。GCC 10.4+工作得很好。最新版本的clang无法编译代码。

问题:

  1. std::views::keys是否保证支持任何对/元组类型,还是我的代码UB?
  2. 如果clang14.0.0无法编译,那么我的代码是否合法C++?
  3. 在c++20中是否有更好的方法来实现这个功能?我看了一会儿std::span,但似乎无法使它正常工作。注意:如果std::views::zip可用的话,我很乐意在std::views::iota中使用它。
代码语言:javascript
复制
#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;
}
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2022-07-30 18:47:03

这是LWG 3502。由于这个特殊问题,您的代码在gcc 10.3上失败了,但是它已经解决了,并且您的代码可以很好地工作在gcc 10.4 (或11.1,等等)上。

问题中的例子应该看起来很熟悉:

代码语言:javascript
复制
std::vector<int> vec = {42};
auto r = vec | std::views::transform([](auto c) { return std::make_tuple(c, c); })
             | std::views::keys;

由于operator* for elements_view被指定为:

代码语言:javascript
复制
constexpr decltype(auto) operator*() const { return get<N>(*current_); }

Nth元素是prvalue时(如该示例和您的示例中所示),这是一个立即悬挂的rvalue引用。问题中的解决方案确保operator*在这些情况下返回一个prvalue,因此一切都正常。

请注意,没有理由写:

代码语言:javascript
复制
return std::views::all(vec) | std::views::transform(fn);

范围适配器已经为你这样做了。你只需写:

代码语言:javascript
复制
return vec | std::views::transform(fn);
票数 2
EN

Stack Overflow用户

发布于 2022-07-30 18:38:56

std::views::keys是否保证支持任何对/元组类型,还是我的代码UB?

views::keys保证在C++20中使用pair/tuple,并保证使用C++23中的tuple-like对象,这要归功于P2165

如果clang14.0.0无法编译,那么我的代码是否合法C++?

你的代码格式很好。

但是,没有必要使用reference_wrapper,一个简单的pair<size_t, T&>就足够了,并且没有必要使用std::views::all(vec)vec | views::transform(fn)将自动将vec转换为view

在c++20中是否有更好的方法来实现这个功能?

在C++20,恐怕不行。在C++23中,您可以编写views::iotaviews::zip来完成这一任务。

代码语言:javascript
复制
const std::vector<int> v{10, 20, 30};
for (const auto& [index, value] : views::zip(views::iota(0uz, v.size()), v))
  std::cout << index << " " << value << "\n";

在C++26中,您可以使用views::enumerate (如果采用)

代码语言:javascript
复制
const std::vector<int> v{10, 20, 30};
for (const auto & e : views::enumerate(v))
  std::cout << e.index << " " << e.value << "\n";
票数 3
EN

Stack Overflow用户

发布于 2022-07-30 19:17:42

可以将std::span用于make_indexed_view,以使其更加通用。当然,修改底层序列的结构(例如调用std::vector::push_back)是未定义的行为。

代码语言:javascript
复制
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;
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73177944

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档