如何使用概念正确约束基于迭代器的函数

How to properly constrain an iterator based function using concepts

我无法理解 header 中定义的概念。我找不到使用它们的好例子。

我尝试在一个简单的函数中使用这些概念来提供更好的错误消息。我不明白为什么 output_iterator 需要一个类型而 input_iterator 没有。此外,我不知道如何使 random_assign_working 函数更通用。

template<typename T, std::input_iterator InputIter, std::output_iterator<T> OutputIter>
requires std::random_access_iterator<InputIter>
auto random_assign(InputIter inputBegin, InputIter inputEnd, OutputIter outBegin) {
  auto inputSize = std::distance(inputBegin, inputEnd);
  for (size_t it = 0; it < inputSize; it++) {
    auto randomIndex = rand()%inputSize;
    auto selectedInput = inputBegin[randomIndex]; // read input
    *outBegin = selectedInput; // write output
    outBegin++;
  }
}

template<std::input_iterator InputIter, std::output_iterator<uint32_t> OutputIter>
requires std::random_access_iterator<InputIter>
auto random_assign_working(InputIter inputBegin, InputIter inputEnd, OutputIter outBegin) {
  auto inputSize = std::distance(inputBegin, inputEnd);
  for (size_t it = 0; it < inputSize; it++) {
    auto randomIndex = rand()%inputSize;
    auto selectedInput = inputBegin[randomIndex]; // read input
    *outBegin = selectedInput; // write output
    outBegin++;
  }
}

int main() {
  {
    std::array<uint32_t, 9> input = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    std::forward_list<uint32_t> output;
    random_assign(input.begin(), input.end(), std::front_insert_iterator(output)); // how can I make this work
    random_assign_working(input.begin(), input.end(), std::front_insert_iterator(output)); // working

    std::stringstream buf {};
    buf << "Out ";
    for (auto item : output)
      buf << " " << item;
    std::cout << buf.str() << std::endl;
  }
  {
    std::array<std::string, 9> input = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
    std::vector<std::string> output;
    output.reserve(input.size());
    random_assign(input.begin(), input.end(), output.begin()); // how can I make this work
    random_assign_working(input.begin(), input.end(), std::front_insert_iterator(output)); // how can I make this work
  }
}

所以我的问题是: 如何在编写函数时利用 中定义的概念。 iterator_tags 如何与此相关联? 如何检查 iterator_tags?

我很感激学习资源并为我指明正确的方向。

I don't understand why the output_iterator takes a type but input_iterator doesn't any.

因为输入迭代器具有具体类型:它是您从 *it 获得的类型。那就是迭代器的引用类型(iter_reference_t<I>).

但输出迭代器没有 - 它们只有一组类型,您可以写入它们。 *out 给你什么并不重要,重要的是你可以将什么类型放入 *out = x; 你不能要求像...给我所有可能的类型,你可以写进我的预期输出迭代器类型,O。但是您可以询问 O 是否接受 specific 类型 T.

例如,int* 是一个输入迭代器,其引用类型为 int&。但是 int* 也可以用作输出迭代器 - 但适用于多种类型。 int 当然,还有 int16_t,或 uint64_t,或 char,或任何其他可转换为 int.

的类型

因此,您可以询问 I 是否是 input_iterator,但您只能询问 O 是否是您正在写入的某些类型的 output_iteratorT.


让我们来看看你的 random_assign:

template<typename T, std::input_iterator InputIter, std::output_iterator<T> OutputIter>
    requires std::random_access_iterator<InputIter>
auto random_assign(InputIter inputBegin, InputIter inputEnd, OutputIter outBegin);

首先,random_access_iterator 优化了 input_iterator,因此您无需同时编写两者。只需选择最具体的约束。一旦你这样做了,你写入 outBegin 的类型就是你的输入迭代器的引用类型,所以这就是你需要的约束:

template <std::random_access_iterator I, std::output_iterator<std::iter_reference_t<I>> O>
void random_assign(I first, I last, O out);

接下来,您想要return有用的信息。在这种情况下,我们要前进 out 很多次,所以我们应该 return 它的新值。

全部内容:

template <std::random_access_iterator I, std::output_iterator<std::iter_reference_t<I>> O>
auto random_assign(I first, I last, O out) -> O {
    // you can use distance, but we know it's random access
    size_t const dist = last - first;

    for (size_t i = 0; i != dist; ++i) {
        // output_iterator requires this work
        *out++ = first[rand() % dist];
    }

    // law of useful return
    return out;
}