Как проверить, стабилен ли контейнер

std::vector является неустойчивым контейнером, то есть путем изменения размера вектора, итераторы могут стать недействительными. Напротив, std::list или boost::container::stable_vector являются стабильными контейнерами, которые сохраняют итераторы действительными до удаления соответствующего элемента.

Есть ли способ проверить, является ли данный контейнер стабильным? Например, если у меня есть что-то вроде

template<template <typename A, typename B=std::allocator<A> > class T=std::list>
class Foo
{
}

Можно ли разрешать только для стабильных контейнеров и запрещать неустойчивые?

Ответы

Ответ 1

Я не думаю, что есть что-то доступное, предоставляющее такую ​​информацию, но вы могли бы написать свою собственную черту. Однако вам нужно будет специализировать его для каждого стабильного контейнера, который может быть использован, что, возможно, не является вариантом.

#include <boost/container/vector.hpp>

#include <iostream>
#include <type_traits>
#include <list>
#include <vector>

template <template <typename...> class Container>
struct is_stable
    : std::false_type
{};

template <>
struct is_stable<std::list>
    : std::true_type
{};

template <>
struct is_stable<boost::container::stable_vector>
    : std::true_type
{};

template<template <typename...> class Container = std::list>
class Foo
{
    static_assert(is_stable<Container>::value, "Container must be stable");
};

int main()
{
    Foo<std::list> f1; // ok
    Foo<std::vector> f2; // compiler error
}

Я не думаю, что вы можете автоматически обнаружить, что контейнер стабилен, не прибегая к ручной специализации.


Просто для удовольствия я попытался написать, как будет выглядеть концепция/аксиома стабильности (понятия и axioms являются расширением языка, который был рассмотрен для включения в С++ 11)

concept StableGroup<typename C, typename Op>
    : Container<C>
{
    void operator()(Op, C, C::value_type);

    axiom Stability(C c, Op op, C::size_type index, C::value_type val)
    {
        if (index <= c.size())
        {
            auto it = std::advance(c.begin(), index);
            op(c, val);
            return it;
        }
        <->
        if (index <= c.size())
        {
            op(c, val);
            return std::advance(c.begin(), index);
        }
    }
}

Если вы думаете, что это правильно отражает требование, чтобы каждый итератор над исходным контейнером был эквивалентен соответствующему итератору над модифицированным контейнером. Не уверен, что это очень полезно, но придумывать такие аксиомы - интересное упражнение:)!