Можно ли специализировать определение шаблона на основе наличия вложенной typedef параметра типа шаблона? - PullRequest
5 голосов
/ 28 сентября 2011

У меня есть шаблон, template <typename T> class wrapper, который я хотел бы специализировать, основываясь на существовании typename T::context_type.Если объявлено typename T::context_type, тогда перегруженные конструкторы и операторы присваивания экземпляра wrapper<T> должны принять обязательный параметр typename T::context_type.Кроме того, wrapper<T> объекты будут хранить «контекст» в данных члена.Если typename T::context_type не существует, тогда перегруженные конструкторы и операторы присваивания wrapper<T> будут принимать на один параметр меньше, и не будет никакого дополнительного элемента данных.

Возможно ли это?Могу ли я получить следующий код для компиляции без изменения определений config1, config2 и main()?

#include <iostream>

template <typename T, bool context_type_defined = true>
class wrapper
{
public:
    typedef typename T::context_type context_type;

private:
    context_type ctx;

public:
    wrapper(context_type ctx_)
        : ctx(ctx_)
    {
        std::cout << "T::context_type exists." << std::endl;
    }
};

template <typename T>
class wrapper<T, false>
{
public:
    wrapper() {
        std::cout << "T::context_type does not exist." << std::endl;
    }
};

class config1 {
public:
    typedef int context_type;
};

class config2 {
public:
};

int main()
{
    wrapper<config1> w1(0);
    wrapper<config2> w2;
}

Ответы [ 3 ]

4 голосов
/ 28 сентября 2011

Это возможно, и есть много способов реализовать это. Все они должны вернуться к некоторому классу признаков has_type, чтобы has_type<T>::value было истинным, если существует член typedef, и ложным в противном случае. Давайте предположим, что у нас уже есть этот класс черт. Тогда вот одно решение, использующее псевдонимы шаблона C ++ 11:

template <typename T, bool> class FooImpl
{
  // implement general case
};

template <typename T> class FooImpl<T, true>
{
  // implement specific case
};

template <typename T> using Foo = FooImpl<T, has_type<T>::value>;  // C++11 only

Теперь, чтобы сделать набег:

template<typename T>
struct has_type
{
private:
    typedef char                      yes;
    typedef struct { char array[2]; } no;

    template<typename C> static yes test(typename C::context_type*);
    template<typename C> static no  test(...);
public:
    static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};

Если у вас нет C ++ 11 или если вы не хотите переписывать весь класс, вы можете сделать это различие более детальным, например, с помощью std::enable_if, std::conditional и т. д. Оставьте комментарий, если вам нужны конкретные примеры.

4 голосов
/ 28 сентября 2011

Да, это возможно.Я реализовывал такое поведение в прошлом, используя некоторые приемы метапрограммирования.Основные блоки сборки:

BOOST_MPL_HAS_XXX_TRAIT_DEF, чтобы определить предикат метафункции, который будет иметь истинный тип, если аргумент имеет тип класса и имеет вложенный тип с заданным именем (context_type в вашем случае).

http://www.boost.org/doc/libs/1_47_0/libs/mpl/doc/refmanual/has-xxx-trait-def.html

Boost.EnableIf, чтобы определить специализации на основе ранее определенной характеристики.

http://www.boost.org/libs/utility/enable_if.html # См. 3.1 Включение специализаций класса шаблона


Обратите внимание, что вы, возможно, сможете заставить это поведение работать напрямую с SFINAE, что-то вроде этого может работать:

template< typename T, typename Context = void >
class wrapper { ... }; // Base definition

template< typename T >
class wrapper< T, typename voif_mfn< typename T::context_type >::type > { ... }; // Specialization

Однако мне нравится выразительность решения, основанного начерты и включить, если.

1 голос
/ 28 сентября 2011

Используя @ K-балл ответ , я написал следующее:

namespace detail {
BOOST_MPL_HAS_XXX_TRAIT_DEF(context_type)
}

template <typename T, typename Enable = void>
class wrapper
{
public:
    wrapper() {
        std::cout << "T::context_type does not exist." << std::endl;
    }
};

template <typename T>
class wrapper<T, typename boost::enable_if<detail::has_context_type<T> >::type>
{
public:
    typedef typename T::context_type context_type;

private:
    context_type ctx;

public:
    wrapper(context_type ctx_)
        : ctx(ctx_)
    {
        std::cout << "T::context_type exists." << std::endl;
    }
};

Теперь пример кода компилируется и выводится:

T::context_type exists.
T::context_type does not exist.
...