Checking if a sequence container is contiguous in memory

后端 未结 3 491
臣服心动
臣服心动 2021-01-07 23:19

Is there a way to check if a sequence container is contiguous in memory? Something like:

#include 
#include 
#include 

        
3条回答
  •  -上瘾入骨i
    2021-01-07 23:59

    No, there is not compiletime trait for this.

    The draft C++1z Standard defines contiguity as a runtime property of an iterator range. Note there is no compiletime std::contiguous_iterator_tag corresponding to this iterator category.

    24.2 Iterator requirements [iterator.requirements]

    24.2.1 In general [iterator.requirements.general]

    5 Iterators that further satisfy the requirement that, for integral values n and dereferenceable iterator values a and (a + n), *(a + n) is equivalent to *(addressof(*a) + n), are called contiguous iterators. [ Note: For example, the type “pointer to int” is a contiguous iterator, but reverse_iterator is not. For a valid iterator range [a,b) with dereferenceable a, the corresponding range denoted by pointers is [addressof(*a),addressof(*a) + (b - a)); b might not be dereferenceable. — end note ]

    One way to test for this at runtime would be

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    template
    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 { 1, 2, 3 };
        auto m = std::map  { {1, 1}, {2,2}, {3,3} };
        auto u = std::unordered_multiset { 1, 1, 1 };
        auto d = std::deque(4000);
        int c[] = { 1, 2, 3 };
        auto a = std::array {{ 1, 2, 3 }};
        auto s = std::string {"Hello world!"};
        auto v = std::vector { 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. This prints false for the list, map and unordered_multimap, and true for the C-array, and the std::array, string and vector. It prints true for small subranges within a deque and false for large subranges. It also prints false for an iterator range consisting of reverse iterators.

    UPDATE: as commented by @T.C. the original N3884 proposal did have a

    struct contiguous_iterator_tag : random_access_iterator_tag {};
    

    so that tag-dispatching on iterator categories would not break. However, this would have broken non-idiomatic code with class template specializations on random_access_iterator_tag. The current draft hence does not contain a new iterator category tag.

提交回复
热议问题