【问题标题】:std::array::iterator that ignores the size template忽略大小模板的 std::array::iterator
【发布时间】:2020-08-18 21:03:48
【问题描述】:

我正在实现一个函数,该函数希望遍历 std::array 中的多个元素,但我并不关心 std::array 有多长。所以我在考虑以下功能:

#include <stdio.h>
#include <array>
#include <iterator>

void foo(std::array<bool,0>::const_iterator begin, std::array<bool,0>::const_iterator end)
{
    printf("iterator");
}

int main()
{
    std::array<bool, 25> one;
    std::array<bool, 33> two;

    foo(one.cbegin(), one.cend());
    foo(two.cbegin(), two.cend());
}

除了std::array&lt;bool,0&gt;,我对此很满意。我的问题是,是否有另一种方法来指定此函数所需的迭代器?


更新

有些事情我应该提一下。当然,这段代码是更大范围的一部分,我试图尽可能多地隐藏细节。

  1. 我想确保使用的迭代器是bools。
  2. 我使用的是 C++14
  3. 该函数是类接口的一部分,我希望能够处理多个数组大小。我不想打扰接口的实现者确切知道数组的大小。
class MyInterface
{
public:
    virtual foo(std::array<bool,0>::const_iterator begin, std::array<bool,0>::const_iterator end) = 0;

    ~MyInterface() = default;
};

我记得虚函数不能被模板化。这意味着我必须对我的整个界面进行模板化,而这将完全失去我最初尝试这个的原因。

【问题讨论】:

  • 您可以提取数组大小作为该函数的模板参数。
  • 您想要std::array&lt;bool, N&gt; 迭代器还是an 迭代器?由于std::array&lt;bool,N&gt; 是连续的,bool* 是它的有效迭代器类型。
  • @MSalters 我正在尝试使用某种现代方法,而不是使用诸如布尔指针之类的简单类型,尽管它可能是我想要实现的最佳解决方案。但我首先在寻找替代品,因此是这个问题。感谢您的建议。
  • @Marnix:“现代方法”意味着我们对new boolnew bool[ ] 使用智能指针。仍然可以使用bool* 作为指向连续bool[ ] 的非拥有指针。不像我们现在使用std::pointer&lt;bool&gt;,虽然在C++20中有std::span&lt;bool&gt;

标签: c++ arrays c++14 const-iterator


【解决方案1】:

你可以把它变成一个函数模板

template <typename I>
void foo(I begin, I end)
{
    std::cout << "iterator";
}

你不需要关心容器类型(和大小),你可以传递std::arraystd::vectorstd::string等迭代器,甚至是原始指针(也满足迭代器的要求)。

【讨论】:

  • 我来自 C# 背景,据我所知,知道容器类型是什么让我非常满意。为什么这是我们可以忽略的?
  • @Marnix 这样做的好处是界面没有受到不必要的限制。有一个算法可以更灵活地处理任何一对迭代器(可能受迭代器类别的限制),而不仅仅是数组迭代器。
  • @Marnix 在 C++ 中迭代器是独立的概念;他们很一般。然后函数模板也变得通用,它可以与来自任何容器的任何迭代器一起使用。您可以检查算法库中的函数模板,它们都只接受迭代器(但不支持容器)。
  • @Marnix 在这种情况下,请参阅不需要模板的 Ayxan 的答案。
  • @Marnix:“我不想模板化界面,因为我希望界面灵活。”模板化界面是如何 我们在 C++ 中使事情变得灵活。不同的语言,不同的规则。至于“为什么这是我们可以忽略的东西?”,您可以忽略它,因为忽略容器是迭代器的全部要点。它们抽象了对容器的访问,因此您不必关心迭代器来自哪个容器。
【解决方案2】:

只需使用span:

#include <array>
#include <span>

class MyInterface {
public:
    virtual void foo(std::span<bool> barr) = 0;

    // interface destructors should be virtual
    virtual ~MyInterface() = default;
};

void bar(MyInterface& interface) {
    std::array<bool, 42> arr;
    interface.foo(arr);
}

如果您无法访问 C++20 编译器,则可以改用 gsl 中的 gsl::span

【讨论】:

    【解决方案3】:

    使用模板:

    template <size_t N>
    void foo(std::array<bool,N>::const_iterator begin, std::array<bool,N>::const_iterator end)
    {
        printf("iterator");
    }
    

    现在只要两个迭代器都来自大小为N 的数组,这个函数就可以工作。


    如果你想接受来自不同大小数组的迭代器,你只需要为第二个迭代器添加另一个模板参数,比如

    template <size_t N, size_t M>
    void foo(std::array<bool,N>::const_iterator begin, std::array<bool,M>::const_iterator end)
    {
        printf("iterator");
    }
    

    【讨论】:

    • IINM 有两个参数没有意义,因为两个迭代器必须指向同一个容器。
    • @Quentin 在这种情况下我同意。对于其他找到此 Q&A 并且可能需要接受不同大小数组的迭代器的人来说更是如此。
    【解决方案4】:

    如果当引用的 std::array 是多余的时函数接受两个迭代器。只需像这样声明函数

    template <class Iterator>
    void foo( Iterator first, Iterator last );
    

    在声明中,您可以命名与函数中使用的迭代器类型相对应的迭代器,例如

    template <class ForwardIterator>
    void foo( ForwardIterator first, ForwardIterator last );
    

    或者代替名称ForwardIterator,您可以使用名称BidirectionalIteratorRandomAccessIterator 进行自我记录。

    如果您需要知道迭代器的值类型,您可以使用不同的方法。例如

    template <class Iterator>
    void foo( Iterator first, Iterator last )
    {
        using value_type = typename std::iterator_traits<Iterator>::value_type;
        if ( first != last )
        {
             value_type item1 = *first;
             // or
             auto item2 = *first;
             //
             const auto &item3 = *first;
             //...             
        }
    }
    

    在这种情况下,您将拥有一个灵活的函数定义。例如,如果将来您将std::array&lt;N, bool&gt; 更改为std::vector&lt;bool&gt;,该功能将照常工作。

    【讨论】:

      猜你喜欢
      • 2019-04-07
      • 2018-08-03
      • 1970-01-01
      • 2014-01-22
      • 2017-02-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多