【问题标题】:How to get the value type from an output iterator?如何从输出迭代器中获取值类型?
【发布时间】:2014-09-26 21:44:41
【问题描述】:

假设我有一个 C 容器(例如 MyContainer),其中包含的对象存储为 void* 指针。遍历这个容器的元素的唯一方法是通过两个接口函数:

  1. getFirstElem(MyContainer const&, void*):输出容器的第一个元素。
  2. getNextElem(MyContainer const&, void*):输出容器的下一个元素。

我想编写一个通用函数,通过上面提到的接口函数遍历这个 C 容器的元素,并将它们的值复制到 C++ 容器中(例如std::vector)。

到目前为止我做了什么:

template<typename OutputIterator>
void
copy_container(MyContainer const &cont, OutputIterator first) {
  typename std::iterator_traits<OutputIterator>::value_type elem;
  if(getFirstElem(cont, &elem)) {
    do {
      *first = elem;
      ++first;
    } while(getNextElem(cont, &elem))    
  }
}

上面的例子适用于普通的迭代器。但是,它无法使用输出迭代器进行编译(例如,copy_container(cont, std::back_inserter(myvector));)。

原因是在参数类型是输出迭代器的情况下,std::iterator_traits::value_type 会导致 void

有没有办法让这个通用函数也适用于输出迭代器?

我知道在 C++11 中可以使用 decltype(例如 decltype(*first))来完成,但我对 C++11 之前的解决方案特别感兴趣,因为我使用的是旧的 C++ 编译器( gcc v4.4.7)。

【问题讨论】:

  • 即使在 C++11 中,decltype(*first) 也不是 proxy 的解决方案原因(std::back_inserter 就是这种情况)。

标签: c++ stl iterator c++03


【解决方案1】:

正如正确观察到的,输出迭代器的value_typevoid。所以除了替换这个没有什么可做的:

typename std::iterator_traits<OutputIterator>::value_type elem;

有了这个

decltype(*first) elem;

(即使标准不保证它会工作 - 代理可能会通过取消引用输出迭代器来返回)。

正如您所说,没有 C++11 解决方案,因此可能需要重新设计。以下是一些选项:

1。传递容器

您可以传递对容器的引用,而不是指向第一个元素的迭代器。看来您想要的只是push_back

template<template<typename,typename> class stlContainer>
void copy_container(
    MyMontainer const &cont, OutputIterator first) 
{ 
    // insertion in stlContainer

那么你所需要的只是一层特征来分配到每个容器的正确插入实现

2。传递一个额外的模板参数

值类型可以是一个额外的模板参数。

template<typename value_type, typename OutputIterator>
void copy_container(MyMontainer const &cont, OutputIterator first) 
{
    value_type elem;
...

【讨论】:

  • decltype(*first) 对我来说仍然是 std::back_inserter (gcc 4.8.1)。它是为代理实现的std::back_inserter::operator =(const value_type&amp;)
  • @Jarod42 这里有什么争议?在back_insert_iteratorraw_storage_iterator 中,运算符 * 返回一个用于“取消引用赋值”的代理对象,通常是对迭代器本身的引用。我的沟通方式不同吗?
  • 这只是一个说明/示例,decltype(*first) 并不适用于所有情况。
【解决方案2】:

你可以使用类型特征和专业化

template <typename IT>
struct it_value_type
{
    typedef typename std::iterator_traits<IT>::value_type elem;
};

template <typename Container>
struct it_value_type<std::back_insert_iterator<Container>>
{
    typedef typename Container::value_type elem;
};

template <typename Container>
struct it_value_type<std::front_insert_iterator<Container>>
{
    typedef typename Container::value_type elem;
};

然后你的代码变成:

template<typename OutputIterator>
void
copy_container(MyContainer const &cont, OutputIterator first) {
    typename it_value_type<OutputIterator>::elem elem;
    if (getFirstElem(cont, &elem)) {
        do {
            *first = elem;
            ++first;
        } while (getNextElem(cont, &elem));
    }
}

【讨论】:

  • 这个解决方案是如何工作的?当您使用back_inserter 调用copy_container 时,编译器将如何决定使用哪个it_value_type 结构,因为template &lt;typename IT&gt; struct it_value_typetemplate &lt;typename Container&gt; struct it_value_type&lt;std::back_insert_iterator&lt;Container&gt;&gt; 都是可能的。
  • @Dejan: std::back_inserterIT 更专业。
【解决方案3】:

有不同的方法可以解决这个问题,我是这样做的:

template <class T>
std::enable_if_t<!std::is_same_v<typename T::container_type::value_type, void>, size_t> read(T first, size_t count)
{
    typedef typename T::container_type::value_type value_type;

我还有一个用于通常的迭代器。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-06-15
    • 1970-01-01
    • 2011-03-02
    • 1970-01-01
    • 1970-01-01
    • 2018-05-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多