Я думал о том, чтобы быть более педантичным в выборе типа данных в блоке кода, где мне нужно будет выбирать между 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 во время компиляции, но подход кажется немного жестким для моей ситуации.
Ниже приведен один из способов определить, является ли 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
, если это так.
Вы на правильном пути, используя 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;
// ...
}
Примечание: переключатель и слой сахарного покрытия могут быть смешаны вместе, однако я подумал, что вам может понравиться увидеть шаги по отдельности.
Если у него нет 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;
Вы можете и вы должны использовать typename some_container::size_type
для этого даже большинство STL компиляторов typedef size_type
как size_t
но, как вы говорите, используя эту технику, вы можете поддерживать пользовательские контейнеры
С помощью @Матье М.ответ в качестве отправной точки, я использую 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>()));
Как и Матье, параметры методов в этом случае должны отличаться, чтобы избежать двусмысленности.