Проверить, является ли переменная итеративной?

Есть ли способ проверить, является ли произвольный тип переменной итеративным?

Итак, чтобы проверить, имеет ли он проиндексированные элементы, или я могу на самом деле зациклить его детей? (Например, использовать foreach?)

Возможно ли создать универсальный шаблон для этого?

Я нашел методы для других языков программирования при поиске. Еще предстоит выяснить, как это сделать в C ++.

20

Решение

Это зависит от того, что вы подразумеваете под «итеративным». Это свободная концепция в C ++, поскольку вы можете реализовать итераторы различными способами.

Если по foreach вы имеете в виду циклы, основанные на диапазонах C ++ 11, тип требует begin() а также end() методы, которые будут определены и возвращать итераторы, которые отвечают на operator!=,
operator++ а также operator*,

Если вы имеете в виду помощника Boost BOOST_FOREACH, то смотрите BOOST_FOREACH Расширяемость.

Если в вашем проекте есть общий интерфейс, от которого наследуются все итерируемые контейнеры, то вы можете использовать C ++ 11 станд :: is_base_of:

struct A : IterableInterface {}
struct B {}
template <typename T>
constexpr bool is_iterable() {
return std::is_base_of<IterableInterface, T>::value;
}
is_iterable<A>(); // true
is_iterable<B>(); // false
6

Другие решения

Вы можете создать черту для этого:

namespace detail
{
// To allow ADL with custom begin/end
using std::begin;
using std::end;

template <typename T>
auto is_iterable_impl(int)
-> decltype (
begin(std::declval<T&>()) != end(std::declval<T&>()), // begin/end and operator !=
void(), // Handle evil operator ,
++std::declval<decltype(begin(std::declval<T&>()))&>(), // operator ++
void(*begin(std::declval<T&>())), // operator*
std::true_type{});

template <typename T>
std::false_type is_iterable_impl(...);

}

template <typename T>
using is_iterable = decltype(detail::is_iterable_impl<T>(0));

Живой пример.

21

да используя это класс черт совместимый

template<typename C>
struct is_iterable
{
typedef long false_type;
typedef char true_type;

template<class T> static false_type check(...);
template<class T> static true_type  check(int,
typename T::const_iterator = C().end());

enum { value = sizeof(check<C>(0)) == sizeof(true_type) };
};

объяснение

  • check<C>(0) звонки check(int,const_iterator) если C::end() существует и возвращает const_iterator совместимый тип
  • еще check<C>(0) звонки check(...) (увидеть эллипсис)
  • sizeof(check<C>(0)) зависит от типа возвращаемого значения этих функций
  • наконец, компилятор устанавливает константу value в true или же false

Смотрите компиляцию и тестовый запуск coliru

#include <iostream>
#include <set>

int main()
{
std::cout <<"set="<< is_iterable< std::set<int> >::value <<'\n';
std::cout <<"int="<< is_iterable< int           >::value <<'\n';
}

Выход

set=1
int=0

Замечания: C ++ 11 (и C ++ 14) предоставляет множество черты классов но не про итеративность …

Смотрите также похожие ответы от Джрок а также Jarod42.

Этот ответ находится в общественном достоянии — CC0 1.0 Универсальный

4

У cpprefence есть пример ответа на ваш вопрос. Он использует SFINAE, Вот немного измененная версия этого примера (в случае, если содержание этой ссылки изменяется со временем):

template <typename T, typename = void>
struct is_iterable : std::false_type {};

// this gets used only when we can call std::begin() and std::end() on that type
template <typename T>
struct is_iterable<T, std::void_t<decltype(std::begin(std::declval<T>())),
decltype(std::end(std::declval<T>()))
>
> : std::true_type {};

// Here is a helper:
template <typename T>
constexpr bool is_iterable_v = is_iterable<T>::value;

Вот как это можно использовать

std::cout << std::boolalpha;
std::cout << is_iterable_v<std::vector<double>> << '\n';
std::cout << is_iterable_v<std::map<int, double>> << '\n';
std::cout << is_iterable_v<double> << '\n';
struct A;
std::cout << is_iterable_v<A> << '\n';

Выход:

true
true
false
false

Сказав это, все это проверяет, декларация begin() const а также end() constсоответственно, даже следующее проверяется как повторяемое:

struct Container
{
void begin() const;
void end() const;
};

std::cout << is_iterable_v<Container> << '\n'; // prints true

Вы можете увидеть эти кусочки вместе Вот

1

Невозможно проверить, является ли произвольный тип переменной итеративным. Хотя есть некоторые распространенные типы, для которых это можно сделать, но проверка будет во время компиляции, а не во время выполнения.

Контейнеры в стиле STL в c ++ предоставляют доступ к элементам begin и end. Наличие этих членов можно использовать для определения вероятности повторяемости типа.

Примитивный тип массива в c ++ вносит некоторую загадку. Адрес первого допустимого индекса и адрес, следующий за последним допустимым индексом, могут использоваться в качестве допустимых начальных и конечных итераторов. Однако массивы c ++ не показывают их длину; Вы должны хранить это в отдельной переменной. Таким образом, чтобы определить «конечный итератор» массива, вам нужно две части информации: адрес первого индекса и длина массива. (На самом деле, вам также необходим третий фрагмент информации: ширина байта каждого индекса, но это, как правило, выводится из самого массива).

-1
По вопросам рекламы [email protected]