【问题标题】:Deduce type of container from iterator (when possible)从迭代器中推断出容器的类型(如果可能)
【发布时间】:2017-08-15 14:37:08
【问题描述】:

是否可以从迭代器类型中检测容器类型?

例如,

#include<traits>
int main(){
   static_assert(std::is_same<
      container_of<std::vector<double>::iterator>::type, std::vector<double>>{});
   static_assert(std::is_same<
      container_of<std::list<int>::iterator>::type, std::list<int>>{});
}

(当然,某些迭代器类型不会给出容器(或不给出唯一容器),例如原始指针或流迭代器,但在这些情况下它可能会软 SFINAE 失败。)

第一次尝试是

template<class T, template<class> class Cont> Cont<T> aux(typename Cont<T>::iterator it);

template<class Iterator> struct container_of{
  using type = decltype(aux(Iterator{}));
};

但是,它不起作用,因为编译器无法检测到T 的类型(它不在可扣除的上下文中)。


动机:我想检测一个迭代器的关联容器是否有.data()成员。

【问题讨论】:

  • 快速回答:检查 .data() 成员的存在将比 container_of trait 更容易实现。
  • 这是完全不可能的,永远不可能。迭代器类型只是某个类中的嵌套类型,或者可能只是没有嵌套在任何地方的类型。如果有的话,没有办法恢复它嵌套在哪个类中。
  • 您可以为 known 容器/迭代器类型编写特征。动机有点不清楚。 data() 对迭代器没有用处,你需要一个容器对象来调用它。
  • 我认为他试图推断底层容器是连续的。
  • @n.m.我知道。重点如下。实际上可能检查一个容器是否是连续的。这是因为,实际上,.data() 成员的存在会泄露这一点。然而,这个技巧不能用于迭代器,即使在原则上显然也是如此。

标签: c++11 iterator containers sfinae typetraits


【解决方案1】:

不要让你的基元成为一个迭代器,而是让你的基元成为一个范围。

template<class It, bool Contiguous, class D=void>
struct range_t {
  using Self = std::conditional< !std::is_same<D, void>, D, range_t >;
  It b, e;
  It begin() const { return b; }
  It end() const { return e; }
  Self without_front( std::size_t i = 1 ) const {
    return {std::next(begin(), i), end()};
  }
  Self without_back( std::size_t i = 1 ) const {
    return {begin(), std::prev(end(), i)};
  }
  bool empty() const { return begin()==end(); }
  std::size_t size() const { return std::distance( begin(), end() ); }
};
template<class It>
struct range_t<It, true, void>:
  range_t<It, false, range_t<It, true>>
{
  using Base = range_t<It, false, range_t<It, true>>;
  range_t( It b, It e ):Base(b,e) {}
  auto* data() const {
    if (empty()) return nullptr;
    return std::addressof(*this->begin()); }
  }
};

跟踪(手动)哪些容器是连续的:

template<class T, class=void>
struct is_contiguous_container : std::false_type{};
template<class T>
struct is_contiguous_container<T const, void> : is_contiguous_container<T> {};
template<class T>
struct is_contiguous_container<T volatile, void> : is_contiguous_container<T> {};
template<class T>
struct is_contiguous_container<T const volatile, void> : is_contiguous_container<T> {};
template<class T>
struct is_contiguous_container<T, std::enable_if_t< has_data_ptr<T>{} >>:
  std::true_type{};
template<class T, std::size_t N>
struct is_contiguous_container<T[N],void> : std::true_type{};

连续的容器是数组,std::arraystd::vector,所以不需要跟踪。 range_t&lt; ?, true, ? &gt; 也是连续的。只需写has_data_ptr,如果T.data() 返回一个指向非void 的指针,则为真。

template<class C>
auto range( C&& c ) {
  using std:begin; using std::end;
  auto b = begin(c), e = end(c);
  using It = decltype(b);
  using R = range_t<It, is_contiguous_container<std::remove_reference_t<C>>{}>;
  return R{ b, e };
}

range 现在可以智能地将容器转换为 range_t,跟踪它是否是连续的。

range_t支持r.without_front( r.size()/2 )分治。

当一个范围是连续的时,只需调用.data()就可以了。如果不是,请不要。

【讨论】:

  • 这或多或少没有我弄清楚,但不是手动跟踪容器,而是手动跟踪迭代器(这需要更复杂的专业化)。但是问题是一样的,迭代器数量非常多std::vectorstd::arrayboost::arrayboost::static_vectorboost::flat_set...。
  • @alfC 对container.data() 的测试返回与std::decay_t&lt;decltype(*std::begin(c))&gt; 类型匹配的指针会捕获该列表的大部分内容。
  • 是的,它有点循环。我认为如果我制作一个使用容器(或某些版本的范围)的界面,那么(由于设计事故)可以检测到来自.data() 的连续性。可悲的是,如果我想将接口基于迭代器,我就不能这样做。也许您建议将接口基于容器/范围而不是指针是正确的。
  • @alfC 使用范围(容器是范围的一种)涵盖了一大堆罪恶。 C++ 标准委员会甚至出现了,Rangv3 是一组基于范围的算法和操作。
【解决方案2】:

在您的应用程序中,如果您只想知道容器是否有.data() 成员,您可能只需检查它是否是随机访问(使用std::iterator_traits&lt;Iter&gt;::iterator_category())。

否则,我认为您可以结合使用以下技术:How to check if two types come from the same templated class 和每个标准容器类型的部分特化。

或者等待具有新的连续迭代器概念的 c++17:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4284.html

【讨论】:

  • 谢谢。 (1) .data()RandomAccess 暗示更多。一些 RandomAccess 迭代器对应于没有 .data() 成员 (std::queue) 的容器 (2) 另外,通过链接我可以检测到迭代器是否是带有一些 Tstd::vector&lt;T&gt;::iterator,但我无法检测到是否它是Container&lt;T&gt;::iterator 和一些Container。 (3) 连续迭代器的概念目前是半生不熟的东西。见这里stackoverflow.com/questions/42851957/…
  • 如果连续迭代器具有.data() 成员或data(...) 函数或可转换为指针,那就太好了。这将以编程方式表明它们是连续的。
【解决方案3】:

我现在正在做的是手动注册所有(一些真的)连续的迭代器。

由于我总是需要结合某种方法来提取原始指针,因此我直接编写了一个名为 data 的函数来返回指针。

代码不好笑,它考虑了std::vector&lt;&gt;::iteratorstd::basric_string&lt;&gt;::iterator,用于说明(以表明它总是不完整的)我还添加了boost::static_vector&lt;&gt;,原始指针和任何可转换为指针的东西。 (boost::array&lt;&gt;::iteratorstd::array&lt;&gt;::iteratorbegin/end(std::valarray) 被有效地包括在内,因为迭代器 指针)。 我还必须包括 const_iterator 案例。

#include<type_traits>
#include<vector> // the code below needs to know about std::vector
#include<boost/container/static_vector.hpp> // ... and all possible contigous containers :(

template<
    class ContiguousIterator, // well ProbablyContiguos
    typename = std::enable_if_t<

        /**/std::is_same<ContiguousIterator, typename std::vector<std::decay_t<decltype(*std::declval<ContiguousIterator>())>>::iterator>{}
        or  std::is_same<ContiguousIterator, typename std::vector<std::decay_t<decltype(*std::declval<ContiguousIterator>())>>::const_iterator>{}

        or  std::is_same<ContiguousIterator, typename std::basic_string<std::decay_t<decltype(*std::declval<ContiguousIterator>())>>::iterator>{}

        or  std::is_same<ContiguousIterator, typename boost::container::static_vector<std::decay_t<decltype(*std::declval<ContiguousIterator>())>, 1>::iterator>{}
        or  std::is_same<ContiguousIterator, typename boost::container::static_vector<std::decay_t<decltype(*std::declval<ContiguousIterator>())>, 1>::const_iterator>{}
        // many many other possible iterators :(

        or  std::is_pointer<ContiguousIterator>{}
        or  std::is_constructible<typename std::iterator_traits<ContiguousIterator>::pointer, ContiguousIterator>{}
    >
>
typename std::iterator_traits<ContiguousIterator>::pointer
data(ContiguousIterator const& it){return std::addressof(*it);}

int main(){
    std::vector<double> v(30);
    v[0] = 10.;
    assert( *data(v.begin()) == 10. );
}

欢迎反馈。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-02
    • 2015-11-06
    相关资源
    最近更新 更多