Определение typedef во время компиляции (метапрограммирование шаблонов)
В настоящее время я выполняю метапрограммирование шаблонов. В моем случае я могу обрабатывать любой "итеративный" тип, т.е. Любой тип, для которого a typedef foo const_iterator
существует таким же образом. Я пытался использовать новый метапрограммирование шаблона С++ 11 для этого, однако я не смог найти метод для определения того, отсутствует ли какой-либо тип.
Поскольку мне также нужно включить/выключить другие специализированные шаблоны, основанные на других характеристиках, в настоящее время я использую шаблон с двумя параметрами, а второй - через std::enable_if
. Вот что я сейчас делаю:
template <typename T, typename Enable = void>
struct Foo{}; // default case is invalid
template <typename T>
struct Foo< T, typename std::enable_if<std::is_fundamental<T>::value>::type>{
void do_stuff(){ ... }
};
template<typename T>
struct exists{
static const bool value = true;
};
template<typename T>
struct Foo<T, typename std::enable_if<exists< typename T::const_iterator >::value >::type> {
void do_stuff(){ ... }
};
Я не смог сделать что-то подобное без exists
вспомогательного шаблона. Например, просто делая
template<typename T>
struct Foo<T, typename T::const_iterator> {
void do_stuff(){ ... }
};
не работает, поскольку в тех случаях, когда эта специализация должна использоваться, вместо этого был создан недействительный случай по умолчанию.
Однако я не смог найти этот exists
в любом месте в новом стандарте С++ 11, который, насколько я знаю, просто берет из boost::type_traits
для такого рода вещей. Однако на домашняя страница для boost::type_traits
не отображается ссылка на все, что можно было бы использовать вместо этого.
Является ли эта функциональность отсутствующей, или я не заметил другого очевидного способа достижения желаемого поведения?
Ответы
Ответ 1
Если вы просто хотите, чтобы данный тип содержал const_iterator
, то следующая - это упрощенная версия вашего кода:
template<typename T>
struct void_ { typedef void type; };
template<typename T, typename = void>
struct Foo {};
template<typename T>
struct Foo <T, typename void_<typename T::const_iterator>::type> {
void do_stuff(){ ... }
};
См. этот ответ для объяснения того, как работает этот метод.
Ответ 2
Вы можете создать признак has_const_iterator
, который предоставляет логическое значение и использовать его в специализации.
Что-то вроде этого может это сделать:
template <typename T>
struct has_const_iterator {
private:
template <typename T1>
static typename T1::const_iterator test(int);
template <typename>
static void test(...);
public:
enum { value = !std::is_void<decltype(test<T>(0))>::value };
};
И тогда вы можете специализироваться следующим образом:
template <typename T,
bool IsFundamental = std::is_fundamental<T>::value,
bool HasConstIterator = has_const_iterator<T>::value>
struct Foo; // default case is invalid, so no definition!
template <typename T>
struct Foo< T, true, false>{
void do_stuff(){// bla }
};
template<typename T>
struct Foo<T, false, true> {
void do_stuff(){//bla}
};
Ответ 3
Здесь другая версия проверки типа элемента:
template<typename T>
struct has_const_iterator
{
private:
typedef char yes;
typedef struct { char array[2]; } no;
template<typename C> static yes test(typename C::const_iterator*);
template<typename C> static no test(...);
public:
static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};
Ответ 4
Существует несколько способов сделать это. В С++ 03 вы можете использовать boost и enable_if
для определения признака (docs, источник):
BOOST_MPL_HAS_XXX_TRAIT_DEF(const_iterator);
template <typename T, typename Enable = void>
struct Foo;
template <typename T>
struct Foo< T, typename boost::enable_if<boost::is_fundamental<T> >::type>{
void do_stuff(){ ... }
};
template<typename T>
struct Foo<T, typename boost::enable_if<has_const_iterator<T> >::type> {
void do_stuff(){ ... }
};
В С++ 11 вы можете использовать Tick следующим образом:
TICK_TRAIT(has_const_iterator)
{
template<class T>
auto require(const T&) -> valid<
has_type<typename T::const_iterator>
>;
};
template <typename T, typename Enable = void>
struct Foo;
template <typename T>
struct Foo< T, TICK_CLASS_REQUIRES(std::is_fundamental<T>::value)>{
void do_stuff(){ ... }
};
template<typename T>
struct Foo<T, TICK_CLASS_REQUIRES(has_const_iterator<T>())> {
void do_stuff(){ ... }
};
Также с Tick вы можете еще больше улучшить признак, чтобы фактически обнаружить, что const_iterator
на самом деле итератор. Итак, скажем, мы определяем простую черту is_iterator
следующим образом:
TICK_TRAIT(is_iterator,
std::is_copy_constructible<_>)
{
template<class I>
auto require(I&& i) -> valid<
decltype(*i),
decltype(++i)
>;
};
Затем мы можем определить признак has_const_iterator
, чтобы проверить, что тип const_iterator
соответствует признаку is_iterator
следующим образом:
TICK_TRAIT(has_const_iterator)
{
template<class T>
auto require(const T&) -> valid<
has_type<typename T::const_iterator, is_iterator<_>>
>;
};