给定这个程序,用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>
;我想我要的是cc4。我不知道我在哪里,或者应该导致一个抽象的B
被构造。如果我是,我完全可以理解这个错误…但是我是…?
我已经阅读了关于std::views::zip
的两篇标准论文,以及围绕reference
s和value_type
的各种讨论,但我没有看到任何可以解释这一点的内容。
所以,要么我错过了标准论文和/或诊断中的一些关键细节(更有可能?)…或者也许在libstdc++
中有一个小精灵(不太可能?),它试图在不需要的时候实例化pair<D, B>
-无论如何,我都希望从精通的人那里得到输入:)
迭代器不仅有reference
(不幸的是命名为CC_11,因为并不总是一个语言引用),它们还有value_type
。在Ranges中,这个模型的很多部分更加形式化。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...>
不是这种情况,其中reference
是tuple<range_reference_t<Rs>...>
,value_type
是tuple<range_value_t<Rs>...>
。
现在,在transform
的例子中,reference
是B&
,value_type
是B
。这很好,因为常见的引用需求将使用B&
(而不是B
),并且您不需要做任何其他尝试构造B
的事情(例如调用ranges::min
或其他东西)。
但是在std::pair<D&, B&>
0的例子中,假设我们实际上只是在做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*
会工作得很好,因为在那里你最终也会得到Abstract*
的value_type
。或者reference_wrapper<Abstract>
,这至少会清楚地表明你没有空指针。