Выбор между size_t и container :: size_type во время компиляции

Я думал о том, чтобы быть более педантичным в выборе типа данных в блоке кода, где мне нужно будет выбирать между size_type size_t в общем или container::size_type для типов контейнеров. Моя проблема в том, что если у меня есть следующий блок кода, я не знаю, как это сделать. Кто-нибудь может там помочь?

template<typename some_container>
int func(some_container& input)
{
//Some code...
//...
decltype(input.size()) variable_x; //Choose this if defined,
size_t                 variable_x; //otherwise choose this
//... Some more code...
}

В этом случае some_container может быть пользовательский контейнер и не обеспечивает size() функция. То, что привело меня к этому размышлению, читало разницу между size_t а также container::size_type в size_t против контейнера :: size_type. Я тоже читаю Определите, является ли тип контейнером STL во время компиляции, но подход кажется немного жестким для моей ситуации.

2

Решение

Ниже приведен один из способов определить, является ли class содержит тип (например, size_type) или нет:

template <typename T>
struct Has_size_type
{
typedef char (&yes)[2];

template <typename C> static yes test(typename C::size_type*);
template <typename> static char test(...);

static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};

И следующий способ выбора между 2 типами:

template<bool> struct Bool;
template<typename T, typename = Bool<true> >
struct Set { typedef size_type type; };
template<typename T>
struct Set<T,Bool<Has_size_type<T>::value> > { typedef typename T::size_type type; };

Изменить началоВот еще один более простой подход:

template<typename T>
struct void_ { typedef void type; };

template<typename T, typename = void>
struct Set
{ typedef size_type type; };

template<typename T>
struct Set<T,typename void_<typename T::size_type>::type>
{ typedef typename T::size_type type; };

Изменить конец.

Итак, наконец, используйте, как показано ниже:

template<typename some_container>
int func(some_container& input)
{
typedef typename Set<some_container>::type type;
}

А сейчас type либо size_type или же some_container::size_type, если это так.

4

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

Вы на правильном пути, используя decltypeхитрость в том, чтобы использовать SFINAE, что легко сделать с помощью шаблонных классов или перегрузок функций. Я покажу функцию так, как это легко в C ++ 11:

// One helper per size_type source
template <typename T>
auto size_type_alt(T const& t) -> decltype(t.size());

auto size_type_alt(...) -> std::size_t;

// The switch between helpers
template <typename T>
auto size_type_switch() -> decltype(size_type_alt(std::declval<T>{}));

// And syntactic sugar
template <typename T>
using size_type = decltype(size_type_switch<T>());

Использование:

template <typename T>
void some_algorithm(T const& t) {
size_type<T> const size = 0;
// ...
}

Примечание: переключатель и слой сахарного покрытия могут быть смешаны вместе, однако я подумал, что вам может понравиться увидеть шаги по отдельности.

5

Если у него нет size_type тогда это не контейнер. Период.

Стандарт требует, чтобы все контейнеры определяли тип с именем size_type, Из N3337, 23.2.1 Общие требования к контейнерам [container.requirements.general]:

Выражение: X::size_type
Тип возврата: целое число без знака
Утверждение: size_type может представлять любое неотрицательное значение
difference_type
Сложность: время компиляции

Таким образом, ваш код может выглядеть просто как:

typename some_container::size_type variable_x;
2

Вы можете и вы должны использовать typename some_container::size_type для этого даже большинство STL компиляторов typedef size_type как size_t но, как вы говорите, используя эту технику, вы можете поддерживать пользовательские контейнеры

0

С помощью @Матье М.ответ в качестве отправной точки, я использую SFINAE, чтобы определить, существует ли T :: size_type, вместо проверки на size(), Очень похоже, просто избегает auto:

/** Fails if T::size_type isn't defined. */
template <class T>
typename T::size_type SizeType(const T &&t);

/** Fallback to a known size. */
template <class T>
std::size_t SizeType(const T &t);

...

using size_type = decltype(SizeType<T>(std::declval<T>()));

Как и Матье, параметры методов в этом случае должны отличаться, чтобы избежать двусмысленности.

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