簡體   English   中英

是否保證 std::views::keys 可以與任何對/元組類型一起正常工作?

[英]Is std::views::keys guaranteed to work correctly with any pair/tuple type?

代碼粘貼在這里 & https://www.godbolt.org/z/qszqYsT4o

我正在嘗試提供與 c++20 視圖兼容的boost::adaptors::indexed功能。 我的主要用例是std::vector ,但我肯定更喜歡通用解決方案。 我震驚地發現std::views::keys沒有按預期正確提取第一個元素。

對於 GCC 10.3,rng1 的rng1是垃圾,而rng2符合預期。 GCC 10.4+ 工作正常。 最新版clang無法編譯代碼。

問題:

  1. std::views::keys是否保證支持任何對/元組類型,還是我的代碼是 UB?
  2. 鑒於 clang 14.0.0 無法編譯,我的代碼是否合法 C++?
  3. 有沒有更好的方法在 c++20 中實現這個功能? 我看了一會兒std::span ,但似乎無法讓它自然地工作。 注意:如果 std::views::iota 可用,我會很樂意將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;
}

std::views::keys是否保證支持任何對/元組類型,還是我的代碼是 UB?

views::keys保證可以在 C++20 中使用pair / tuple ,並且由於P2165保證可以在 C++23 中使用tuple-like對象。

鑒於 clang 14.0.0 無法編譯,我的代碼是否合法 C++?

您的代碼格式正確。

但是,沒有必要使用reference_wrapper ,一個簡單的pair<size_t, T&>就足夠了,也沒有必要使用std::views::all(vec) , vec | views::transform(fn) vec | views::transform(fn)將自動將vec轉換為view

有沒有更好的方法在 c++20 中實現這個功能?

在 C++20 中,恐怕不是。 在 C++23 中,您可以編寫views::iotaviews::zip來執行此操作,例如

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 (如果采用)

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

這是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);

可以將std::span用於make_indexed_view以使其更通用。 當然,修改底層序列的結構(例如調用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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM