当给定抽象基类的引用 View 时,C++23 std::views::zip 错误

标签 c++ abstract-class std-pair std-ranges c++23

给定这个程序,用 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其中压缩范围之一应该是对抽象基类的引用......只是不起作用。错误如下:

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待 build 。如果我...,我会完全理解这个错误,但是...?

我已经阅读了 2 篇关于 std::views::zip 的标准论文以及围绕reference问题的各种讨论和 value_type ,但我没有看到任何可以解释这一点的内容。

所以,要么我错过了标准论文和/或诊断中的一些关键细节(更有可能?)...或者libstdc++中可能有一个小鬼。 (不太可能?)尝试实例化 pair<D, B>当不需要时 - 不管怎样,我很感激来自更精通的人的输入:)

最佳答案

迭代器不仅仅有 reference (不幸的是,因为并不总是语言引用),它们还有一个 value_type 。在 Ranges 中,很多模型都更加正式化。 C++20输入迭代器的核心概念是 indirectly_readable ,其中关键部分基本上是:

  • 迭代器有 value_type
  • 迭代器有 reference ,这是通过执行 *ci 得到的类型在const I上(注意 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 ,但最近进行了更改,始终使用 std::tuple ,这里的区别并不重要)。


那么,好吧,我们实际上能做什么?实际上没有另一个value_type我们可以生产。重复利用tuple<B&>是一个坏主意,这会破坏您使用的任何实际上想要具有类型的算法。

唯一的选择是...没有 value_type都在这里。有相当多的算法不使用 value_type (其中最简单的就是您想要执行的 for 循环),并且不可否认,由于您没有使用的要求而无法支持这些算法,这有点糟糕。但这就是目前存在的设计,我认为很难改变。


所以解决方案实际上就是尝试避免 Abstract& 的范围。 Abstract*会工作得很好,因为你最终会得到 value_typeAbstract*还。或reference_wrapper<Abstract> ,这至少可以表明您没有空指针。

关于当给定抽象基类的引用 View 时,C++23 std::views::zip 错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75189622/

相关文章:

c# - JsonConvert反序列化抽象类数组

python - Python中抽象类和接口(interface)的区别

c++ - 从模板继承?

c++ - 如何减少 priority_queue<PI, vector<PI> ,greater<PI>> 中特定边的键,试图实现 prim 的算法?

c++ - 在编译时构建类型列表 - 无 C++11

c++ - 什么是好的游戏随机数生成器?

c++ - 构造一对 unique_ptr<int> 和 int

c++ - 使用 pair 创建 priority_queue,当第一个元素相等时,第一个元素的排序为 "<",第二个元素的排序为 ">"

c++ - 如何在 bazel BUILD 文件中指定 -std=c++11 选项?

c++ - ios::base:精度 & 设置精度