Политика и дизайн по умолчанию

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

template < typename T, template <typename,typename> class Policy = default_policy >
struct policy_based : Policy<T, policy_based<T,Policy> >
{
  // inherits R Policy::fun(arg0, arg1, arg2,...,argn)
};

// normal use:
policy_base<type_a> instance;

// abnormal use:
template < typename PolicyBased > // No T since T is always the same when you use this
struct custom_policy {};

policy_base<type_b,custom_policy> instance;

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

У меня есть пара идей, но ни одна из них не является фаворитом. Я подумал, что у меня есть лучший ответ - использование композиции вместо политик - но потом я понял, что у меня есть такой случай, когда fun() действительно нужна дополнительная информация, которой сам класс не будет иметь.

Это как третий раз, когда я реорганизовал эту глупую конструкцию, и у меня есть немало пользовательских версий, которые я пытаюсь объединить. Я хотел бы что-то прибить на этот раз, а не просто ловить рыбу и надеюсь, что это сработает в этот раз. Так что сейчас я просто ищу идеи, надеясь, что у кого-то есть что-то, что меня так впечатлит, что я поменяю божество. У кого-нибудь есть хорошая идея?

Изменить: Вы можете спросить себя, почему я не просто получить T из определения политики на основе шаблона для default_policy. Причина в том, что default_policy на самом деле специализируется на некоторых типах T. После того, как я задал вопрос, я нашел кое-что, что может быть тем, что мне нужно, что последует, но я все еще мог бы использовать некоторые другие идеи.

template < typename T >
struct default_policy;

template < typename T, template < typename > class Policy = default_policy >
struct test : Policy<test<T,Policy>>
{};


template < typename T >
struct default_policy< test<T, default_policy> >
{
  void f() {}
};

template < >
struct default_policy< test<int, default_policy> >
{
  void f(int) {}
};

Редактировать: все еще возиться с этим. Я не слишком любил вышесказанное, поскольку он делает default_policy постоянно связанным с "test" и поэтому не может быть повторно использован в каком-либо другом методе, например, с несколькими шаблонами, как предложено ниже. Он также вообще не масштабируется и требует список параметров, по крайней мере, до тех пор, пока "test" имеет. Попробовал несколько разных подходов, которые потерпели неудачу, пока я не нашел другой, который, кажется, работает до сих пор:

template < typename T >
struct default_policy;

template < typename T, template < typename > class Policy = default_policy >
struct test : Policy<test<T,Policy>>
{};

template < typename PolicyBased >
struct fetch_t;

template < typename PolicyBased, typename T > struct default_policy_base;

template < typename PolicyBased >
struct default_policy : default_policy_base<PolicyBased, typename fetch_t<PolicyBased>::type> {};

template < typename T, template < typename > class Policy >
struct fetch_t< test<T,Policy> > { typedef T type; };

template < typename PolicyBased, typename T >
struct default_policy_base
{
  void f() {}
};

template < typename PolicyBased >
struct default_policy_base<PolicyBased,int>
{
  void f(int) {}
};

2 ответа

Вы смотрели на так называемые параметры именованных шаблонов? Это позволяет вам иметь множество параметров со скрытым значением по умолчанию, каждый из которых может быть переопределен по имени (т.е. в произвольном порядке). Это трюк, основанный на нескольких уровнях косвенности и множественном наследовании. Это описано в главе 16.1 книги "Шаблоны полного руководства" Vandevoorde & Josuttis. Для онлайн-экспозиции смотрите здесь. Идея реализована в Boost.Parameter

Вот краткое резюме для библиотеки Boost.Parameter.

Шаг 1) вы объявляете каждый параметр par0 через parN через следующие макросы:

BOOST_PARAMETER_TEMPLATE_KEYWORD(par0)
BOOST_PARAMETER_TEMPLATE_KEYWORD(par1)
// ... 
BOOST_PARAMETER_TEMPLATE_KEYWORD(parN)    

Каждый макрос будет определять как обычный класс par0 в пространстве имен tag и шаблон класса par0 в области имен

namespace tag { struct par0; } // keyword tag type
template <class T>
struct par0
: 
    parameter::template_keyword<tag::par0, T>
{};

Шаг 2) вы объявляете свою подпись класса с обязательными и необязательными параметрами:

using boost::mpl::_;

typedef parameter::parameters<
    parameter::required<tag::par0>
  , parameter::optional<tag::par1>
  // ...
  , parameter::optional<tag::parN>
> your_signature;

Шаг 3) вы объявляете свой класс политики

template <
    class a0
  , class a1 = parameter::void_
  // ...
  , class aN = parameter::void_
>
struct your_policy
{
    // Create ArgumentPack
    typedef typename
      your_signature::bind<a0, a1, /* ... */ ,aN>::type
    args;

    typedef typename parameter::value_type<
      args, tag::par0>::type par0;

    typedef typename parameter::value_type<
      args, tag::par1, your_par1_default>::type par1;

    // ...

    typedef typename parameter::value_type<
      args, tag::parN, your_parN_default>::type parN;
};

Шаг 4) Определите ваши политики и специализации

template<typename T>
class default_policy
:
    your_policy<T> // all hidden template parameters equal to their defaults
{};

template<typename T>
class some_par2_specialized_policy
:
    your_policy<T, par2<some_override_for_par2> > // note par1 does not have to be overriden!!
{};

Для гибкого и масштабируемого дизайна на основе политик мой опыт показывает, что Boost.Parameter работает как чудо (вам может потребоваться переопределить BOOST_PARAMETER_MAX_ARITY если у вас есть более 8 скрытых параметров, хотя).

Должна быть возможность адаптировать свой дизайн к этой технике. Конечно, как только вы вводите дополнительные параметры шаблона со скрытыми значениями по умолчанию, вам нужно выделить случаи, когда они переопределяют код, который использует ваш класс политики. Это ничем не отличается от случая, когда вы используете несколько признаков для передачи своего специализированного поведения клиентскому коду, за исключением того, что с политиками у вас все хорошо объединено.

У меня была похожая проблема, и я не нашел желаемого ответа. Из того, что я знаю, C++ не имеет элегантной поддержки переменного количества аргументов шаблона, поэтому вы должны обойти это, "обернув" дополнительные аргументы в другом классе;

policy_base< twoArgs<part1, part2> >

Вы можете сделать это немного лучше, добавив typedefs для twoArgs, но не намного в этом базовом случае. (Помните, что вы можете "делать" шаблонные определения типов, используя члены typedefs в шаблонных классах.)

или имея много разных объявлений основного шаблона, с разными аргументами;

template< typename T1 > struct base {...}
template< typename T1, typename t2 > struct base {...}
//etc
Другие вопросы по тегам