【问题标题】:Checking if a sequence container is contiguous in memory检查序列容器在内存中是否连续
【发布时间】:2016-05-02 11:20:36
【问题描述】:

有没有办法检查序列容器在内存中是否连续?比如:

#include <iostream>
#include <vector>
#include <deque>
#include <array>

int main()
{
    std::cout << std::boolalpha;
    std::cout << is_contiguous<std::vector<int>>::value   << '\n'  // true
    std::cout << is_contiguous<std::deque<int>>::value    << '\n'; // false
    std::cout << is_contiguous<std::array<int, 3>>::value << '\n'; // true
}

澄清

这个问题是指类型特征,而不是类型的特定实例的属性。

【问题讨论】:

  • 目前没有。术语“连续迭代器”仅定义为使规范更紧凑的编辑工具,但它不是可查询的特征。
  • 您仍然可以创建具有所有必需专业化的自定义特征...
  • @BaummitAugen 可能的用例可能是将流输入提取到模板化序列容器中。使用连续容器,可以直接使用std::basic_istream::getline(例如in.getline(&amp;container[0], 10))。
  • @Daniel:这就是像insert_iterator 这样的假迭代器的用途
  • .data() 的存在返回一个指针可能是一个合理的代理。

标签: c++ c++11 containers c++14 typetraits


【解决方案1】:

,没有编译时特性。

draft C++1z Standard 将连续性定义为迭代器范围的运行时属性。注意没有编译时间std::contiguous_iterator_tag对应这个迭代器类别。

24.2 迭代器要求 [iterator.requirements]

24.2.1 一般 [iterator.requirements.general]

5 个迭代器,进一步满足以下要求,对于积分 值n 和可取消引用的迭代器值a(a + n), *(a + n) 相当于*(addressof(*a) + n),称为连续 迭代器。 [注:例如,类型“pointer to int”是一个 连续迭代器,但reverse_iterator&lt;int *&gt; 不是。对于一个有效的 迭代器范围[a,b) 和可取消引用的a,对应的范围 用指针表示的是[addressof(*a),addressof(*a) + (b - a));b 可能无法取消引用。 ——尾注]

在运行时对此进行测试的一种方法是

#include <array>
#include <deque>
#include <list>
#include <iostream>
#include <iterator>
#include <map>
#include <memory>
#include <string>
#include <unordered_set>
#include <vector>

template<class I>
auto is_contiguous(I first, I last)
{ 
    auto test = true;
    auto const n = std::distance(first, last);
    for (auto i = 0; i < n && test; ++i) {
        test &= *(std::next(first, i)) == *(std::next(std::addressof(*first), i));
    }        
    return test;        
}

int main()
{
    auto l = std::list<int> { 1, 2, 3 };
    auto m = std::map<int, int>  { {1, 1}, {2,2}, {3,3} };
    auto u = std::unordered_multiset<int> { 1, 1, 1 };
    auto d = std::deque<int>(4000);
    int c[] = { 1, 2, 3 };
    auto a = std::array<int, 3> {{ 1, 2, 3 }};
    auto s = std::string {"Hello world!"};
    auto v = std::vector<int> { 1, 2, 3, };

    std::cout << std::boolalpha << is_contiguous(l.begin(), l.end()) << "\n";
    std::cout << std::boolalpha << is_contiguous(m.begin(), m.end()) << "\n";
    std::cout << std::boolalpha << is_contiguous(u.begin(), u.end()) << "\n";
    std::cout << std::boolalpha << is_contiguous(d.begin(), d.end()) << "\n";
    std::cout << std::boolalpha << is_contiguous(d.begin(), d.begin() + 1000) << "\n";
    std::cout << std::boolalpha << is_contiguous(std::begin(c), std::end(c)) << "\n";
    std::cout << std::boolalpha << is_contiguous(a.begin(), a.end()) << "\n";
    std::cout << std::boolalpha << is_contiguous(s.begin(), s.end()) << "\n";
    std::cout << std::boolalpha << is_contiguous(v.begin(), v.end()) << "\n";
    std::cout << std::boolalpha << is_contiguous(v.rbegin(), v.rend()) << "\n";
}

Live Example。这将打印false 用于listmapunordered_multimap,以及true 用于C 阵列,以及std::arraystringvector。它为deque 内的小子范围打印true,为大子范围打印false。它还为由反向迭代器组成的迭代器范围打印false

更新:由@T.C. 评论。最初的N3884 提案确实有一个

struct contiguous_iterator_tag : random_access_iterator_tag {};

以便迭代器类别上的标签调度不会中断。但是,这会破坏具有random_access_iterator_tag 上的类模板特化的非惯用代码。因此,当前草案不包含新的迭代器类别标签。

【讨论】:

  • 您忘了提到作为迭代器类别的连续迭代器还不是任何已发布标准的一部分。所以对于 C++11 和 C++14,这个概念不能被特征或其他东西查询。
  • 另外值得注意的是std::deque会在达到一定大小之前返回true,之后会返回false。
  • @ildjarn 更新了对deque(大小子范围)和反向迭代器的测试。
  • 最初的提案会添加一个contiguous_iterator_tag,但最终没有这样做,因为它会破坏现有代码。
  • @T.C.是的,但这仍然会让deque 陷入困境。
【解决方案2】:

没有。 C++ 标准保证没有误报。 (即std::vectorstd::stringstd::array,基本数组承诺连续存储)。

但是,C++ 标准保证没有误报。

int main() {
   std::unique_ptr<Node> n1(new Node);
   std::unique_ptr<Node> n2(new Node);
   n1->next = n2; // n1 and n2 might be contiguous, but might not be
}

因此,您的类型特征有时可能是错误的。如果有时它是错误的,那不是类型特征;相反,它是一个实例特征。

【讨论】:

  • 这样实现的特征无论如何对于其中包含零个或一个元素的任何容器都是不可用的。可能是为什么没有人建议它。所以我们回到了“不”:这种特征可能存在的唯一方法是内置到标准库中,而没有。
  • 但这真的是假的吗?如果我验证容器中除第一个元素之外的每个元素都与前一个元素相邻,那么 那个特定容器在那个时刻 不是连续的吗? (而且我知道如果我不对容器进行任何更改,它也不会改变。)
  • @rici:是的,但这将是您的容器实例的属性,而不是其类型的特征。因此,不是所要求的。
  • @lightness:实际上,看看 OP 中的示例,我可以看到您的解释力度。
  • 我不同意。而“这个容器的数据是连续存储的吗?”类型特征是不可能的,“此容器的数据是否保证连续存储?”很合理,可能有用。
【解决方案3】:

没有。​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​ p>

【讨论】:

  • @WaiHaLee 我会要求你证明这一点。当你得到正确的好处并拒绝时,除了说“检查标准 - 它不在那里”之外,没有什么可以证明的。
  • @WaiHaLee:我无法证明是否定的。这个问题真的被打破了。
  • @LightnessRacesinOrbit 实际上,您可以证明是否定的。两种典型的方法是提供缺乏证据(即上面提到的:这不是标准)或提供不可能的证明(我的回答)。人们不喜欢简单的答案。
  • @erip - 没有证据并不能证明是负面的。
  • 哎呀,我的意思是缺席的证据。
猜你喜欢
  • 2019-06-10
  • 1970-01-01
  • 2012-04-11
  • 2016-09-30
  • 2016-09-05
  • 2011-10-11
  • 1970-01-01
  • 2020-06-24
  • 1970-01-01
相关资源
最近更新 更多