簡體   English   中英

C++23 std::views::zip 給出對抽象基的引用的視圖時出錯 class

[英]C++23 std::views::zip errors when given a view of references to abstract base class

給定這個程序,用g++-13 -std=c++2b test.cpp

……那是哪里……

g++-13 (Debian 13-20230106-1) 13.0.0 20230106 (experimental) [master r13-5040-g53add162511]
#include <array>
#include <ranges>

auto
main() -> int
{
    struct B    { virtual void foo() = 0; };
    struct D: B { virtual void foo() {}   };

    auto ds = std::array<D, 10>{};
    auto to_b = [](D const& d) -> B const& { return d; };
    auto bs = ds | std::views::transform(to_b);

    for (auto& b: bs) {} // OK

    auto zip = std::views::zip(ds, bs);

    for (auto&& pair: zip) {} // error

    return 0;
}

嘗試遍歷std::views::zip ,其中一個壓縮范圍應該是對抽象基礎 class 的引用...只是行不通。 錯誤是這些:

In file included from /usr/include/c++/13/bits/stl_algobase.h:64,
                 from /usr/include/c++/13/array:43,
                 from test.cpp:1:
/usr/include/c++/13/bits/stl_pair.h: In instantiation of ‘struct std::pair<main()::D, main()::B>’:
/usr/include/c++/13/type_traits:3744:12:   recursively required by substitution of ‘template<class _Tp1, class _Tp2> struct std::__common_reference_impl<_Tp1&&, _Tp2&, 1, std::void_t<typename std::__common_ref_impl<_Tp1&&, _Tp2&, void>::type> > [with _Tp1 = std::pair<main()::D&, const main()::B&>; _Tp2 = std::pair<main()::D, main()::B>]’
/usr/include/c++/13/type_traits:3744:12:   required from ‘struct std::common_reference<std::pair<main()::D&, const main()::B&>&&, std::pair<main()::D, main()::B>&>’
/usr/include/c++/13/type_traits:3724:11:   required by substitution of ‘template<class ... _Tp> using std::common_reference_t = typename std::common_reference::type [with _Tp = {std::pair<main()::D&, const main()::B&>&&, std::pair<main()::D, main()::B>&}]’
/usr/include/c++/13/bits/iterator_concepts.h:324:13:   required by substitution of ‘template<class _Iterator>  requires (__iter_without_nested_types<_Iterator>) && (__cpp17_input_iterator<_Iterator>) struct std::__iterator_traits<_Iter, void> [with _Iterator = std::ranges::zip_view<std::ranges::ref_view<std::array<main()::D, 10> >, std::ranges::transform_view<std::ranges::ref_view<std::array<main()::D, 10> >, main()::<lambda(const main()::D&)> > >::_Iterator<false>]’
/usr/include/c++/13/bits/stl_iterator_base_types.h:177:12:   required from ‘struct std::iterator_traits<std::ranges::zip_view<std::ranges::ref_view<std::array<main()::D, 10> >, std::ranges::transform_view<std::ranges::ref_view<std::array<main()::D, 10> >, main()::<lambda(const main()::D&)> > >::_Iterator<false> >’
/usr/include/c++/13/bits/iterator_concepts.h:211:4:   required by substitution of ‘template<class _Iter, class _Tp>  requires  __primary_traits_iter<_Iter> struct std::__detail::__iter_traits_impl<_Iter, _Tp> [with _Iter = std::ranges::zip_view<std::ranges::ref_view<std::array<main()::D, 10> >, std::ranges::transform_view<std::ranges::ref_view<std::array<main()::D, 10> >, main()::<lambda(const main()::D&)> > >::_Iterator<false>; _Tp = std::incrementable_traits<std::ranges::zip_view<std::ranges::ref_view<std::array<main()::D, 10> >, std::ranges::transform_view<std::ranges::ref_view<std::array<main()::D, 10> >, main()::<lambda(const main()::D&)> > >::_Iterator<false> >]’
/usr/include/c++/13/bits/iterator_concepts.h:224:13:   required by substitution of ‘template<class _Iter, class _Tp> using std::__detail::__iter_traits = typename std::__detail::__iter_traits_impl::type [with _Iter = std::ranges::zip_view<std::ranges::ref_view<std::array<main()::D, 10> >, std::ranges::transform_view<std::ranges::ref_view<std::array<main()::D, 10> >, main()::<lambda(const main()::D&)> > >::_Iterator<false>; _Tp = std::incrementable_traits<std::ranges::zip_view<std::ranges::ref_view<std::array<main()::D, 10> >, std::ranges::transform_view<std::ranges::ref_view<std::array<main()::D, 10> >, main()::<lambda(const main()::D&)> > >::_Iterator<false> >]’
/usr/include/c++/13/bits/iterator_concepts.h:227:13:   required by substitution of ‘template<class _Tp> using std::__detail::__iter_diff_t = typename std::__detail::__iter_traits_impl<_Tp, std::incrementable_traits<_Iter> >::type::difference_type [with _Tp = std::ranges::zip_view<std::ranges::ref_view<std::array<main()::D, 10> >, std::ranges::transform_view<std::ranges::ref_view<std::array<main()::D, 10> >, main()::<lambda(const main()::D&)> > >::_Iterator<false>]’
/usr/include/c++/13/bits/iterator_concepts.h:232:11:   required by substitution of ‘template<class _Tp> using std::iter_difference_t = std::__detail::__iter_diff_t<typename std::remove_cvref<_Tp>::type> [with _Tp = std::ranges::zip_view<std::ranges::ref_view<std::array<main()::D, 10> >, std::ranges::transform_view<std::ranges::ref_view<std::array<main()::D, 10> >, main()::<lambda(const main()::D&)> > >::_Iterator<false>]’
/usr/include/c++/13/ranges:4436:26:   required from ‘constexpr auto std::ranges::zip_view<_Vs>::end() requires !((__simple_view<_Vs> && ...)) [with _Vs = {std::ranges::ref_view<std::array<main()::D, 10> >, std::ranges::transform_view<std::ranges::ref_view<std::array<main()::D, 10> >, main()::<lambda(const main()::D&)> >}]’
test.cpp:18:20:   required from here
/usr/include/c++/13/bits/stl_pair.h:194:11: error: cannot declare field ‘std::pair<main()::D, main()::B>::second’ to be of abstract type ‘main()::B’
  194 |       _T2 second;                ///< The second member
      |           ^~~~~~
test.cpp:7:16: note:   because the following virtual functions are pure within ‘main()::B’:
    7 |         struct B    { virtual void foo() = 0; };
      |                ^
test.cpp:7:36: note:     ‘virtual void main()::B::foo()’
    7 |         struct B    { virtual void foo() = 0; };

這樣對嗎? 我不認為我要的是std::pair<D, B> 我想我要的是std::pair<D&, B&> 我看不到我在哪里或應該在哪里導致構建抽象B 如果我……我會完全理解這個錯誤,但是……嗎?

我已經閱讀了關於std::views::zip的 2 篇標准論文以及關於reference s 和value_type問題的各種討論,但我沒有看到任何可以解釋這一點的內容。

所以,要么我錯過了標准論文和/或診斷中的一些關鍵細節(更有可能?)......或者libstdc++中可能有一個 gremlin(不太可能?)試圖在沒有時實例化pair<D, B>需要 - 無論如何我都會感謝精通的人的意見:)

迭代器不僅有reference (不幸的是命名,因為並不總是語言引用),它們還有一個value_type 在 Ranges 中,很多 model 都更加正式。 C++20輸入迭代器的核心概念是indirectly_readable ,其中關鍵部分基本上是:

  • 一個迭代器有一個value_type
  • 一個迭代器有一個reference ,這是你通過在const I上執行*ci得到的類型(注意const 。還要注意*i*ci必須是相同的類型)
  • reference&&value_type&之間有一個共同的引用(注意&

您還可以看到其他要求,但這些是此處最相關的要求。 通常(但並不總是), value_type只是remove_cvref_t<reference> 不是這種情況的一種情況是zip_view<Rs...> ,其中referencetuple<range_reference_t<Rs>...>value_typetuple<range_value_t<Rs>...>

現在,對於transform示例,您的referenceB&並且value_typeB 這很好,因為通用參考要求將使用B& (而不是B ),並且您沒有做任何其他試圖構造B的事情(例如調用ranges::min或其他東西)。

但是對於zip示例,假設實際上我們只是在執行zip(bs) 在這里, reference將是tuple<B&> ,但是value_type是什么? 好吧,它有點必須是tuple<B> ,但你不能制作tuple<B> ,所以它的格式不正確(在實際的 OP 中它是一個std::pair ,但最近對 always 進行了更改使用std::tuple ,區別在這里並不重要)。


那么,好吧,我們實際上可以做什么? 實際上我們無法生成另一種value_type 重用tuple<B&>是一個壞主意,它會破壞您使用的任何實際上想要具有類型的算法。

唯一的選擇是......這里根本沒有value_type 有相當多的算法不使用value_type (其中最簡單的就是您要嘗試執行的for循環),並且不可否認,由於要求而無法支持這些算法有點糟糕。 .. 你沒有使用。 但這就是目前存在的設計,我認為它很難改變。


所以解決方案實際上是盡量避免Abstract&的范圍。 Abstract*會工作得很好,因為你最終也會得到Abstract*value_type reference_wrapper<Abstract> ,這至少會表明您沒有 null 指針。

暫無
暫無

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

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