康桓瑋*_*康桓瑋 17 c++ language-lawyer c++20 std-ranges
在 C++20 中,某些范围同时具有const和 non- const begin()/end(),而其他范围仅具有 non- const begin()/end()。
为了使包装前者的范围适配器能够begin()/end()在合格时使用const,一些范围适配器如elements_view、reverse_view和common_view都提供了 constrainedconst合格的begin()/end()函数,例如:
 template<view V>\n   requires (!common_\xc2\xadrange<V> && copyable<iterator_t<V>>)\n class common_view : public view_interface<common_view<V>> {\n  public:\n   constexpr auto begin();\n   constexpr auto end();\n\n   constexpr auto begin() const requires range<const V>;\n   constexpr auto end()   const requires range<const V>;\n};\n\ntemplate<input_\xc2\xadrange V, size_t N>\n  requires view<V> && has-tuple-element<range_value_t<V>, N>\nclass elements_view : public view_interface<elements_view<V, N>> {\n public:\n  constexpr auto begin();\n  constexpr auto end();\n\n  constexpr auto begin() const requires range<const V>;\n  constexpr auto end()   const requires range<const V>;\n};\n仅当满足begin() const/end() const时才会被实例化,这似乎是合理的。const Vrange
但只有一个简单的约束似乎还不够。举common_view个例子,它要求V不是common_range. 但是当V不是 acommon_range且const V是 acommon_range时(我知道这样的范围极其奇怪,但理论上它是可以存在的,godbolt):
#include <ranges>\n\nstruct weird_range : std::ranges::view_base {\n  int* begin();\n  const int* end();\n\n  std::common_iterator<int*, const int*> begin() const;\n  std::common_iterator<int*, const int*> end()   const;\n};\n\nint main() {\n  weird_range r;\n  auto cr = r | std::views::common;\n  static_assert(std::ranges::forward_range<decltype(cr)>); // ok\n  cr.front(); // ill-formed\n}\n在上面的例子中,const V仍然满足这个range概念,当我们应用r到时views::common,它的front()函数将是病态的。
原因是view_interface::front() const仍然会被实例化,并且会在ofcommon_iterator内部构造,这将导致硬错误中止编译,因为它本身是。begin() constcommon_viewconst Vcommon_range
同样,我们也可以基于相同的概念创建一个奇怪的范围来使 offront()和views::reversefailed views::keys( godbolt ):
#include <ranges>\n\nstruct my_range : std::ranges::view_base {\n  std::pair<int, int>* begin();\n  std::pair<int, int>* end();\n\n  std::common_iterator<int*, const int*> begin() const;\n  std::common_iterator<int*, const int*> end()   const;\n};\n\nint main() {\n  my_range r;\n  auto r1 = r | std::views::reverse;\n  static_assert(std::ranges::random_access_range<decltype(r1)>); // ok\n  r1.front(); // ill-formed\n\n  auto r2 = r | std::views::keys;\n  static_assert(std::ranges::random_access_range<decltype(r2)>); // ok \n  r2.front();  // ill-formed\n}\n那么,范围适配器的const过载是否受到限制,或者其本身的定义格式不正确?这可以被视为标准缺陷吗?begin()/end()weird_range
这个问题主要是受到LWG 3592的启发,LWG 3592指出,对于,我们需要考虑不是的lazy_split_view情况,然后我随后提交了LWG 3599。当我进一步审查其他系列适配器时,我发现它们中的大多数只需要const Patternrangebegin() constconst Vrange这个看似宽松的约束让我提出了这个问题。
为了启用范围适配器begin() const,理论上, 的约束const V应该与 完全相同V,这意味着上的一长串约束V,例如elements_view,需要替换为 而const V不是仅const V将约束替换为range。
但实际上,标准似乎对迭代器和哨兵类型V和 的情况不感兴趣。const V的迭代器和哨兵类型有很大不同的情况不感兴趣。
| 归档时间: | 
 | 
| 查看次数: | 699 次 | 
| 最近记录: |