В моем программном обеспечении, где часто используется метапрограммирование шаблонов, классы шаблонов часто принимают шаблоны классов в качестве аргументов, которые определяют определенные аспекты их поведения. В качестве очень простого примера, предположим, у нас есть класс FormulaUser
которая должна использовать формулу для вычисления числа из двух других чисел, а конкретная формула должна быть указана в качестве параметра шаблона. Кроме того, формула должна быть открытой в отношении того, какой тип данных он использует (с плавающей или двойной). Например:
template <template<typename> class Formula, typename FpType>
struct FormulaUser {
using TheFormula = Formula<FpType>;
void some_function ()
{
FpType x = 1;
FpType y = 2;
FpType result = TheFormula::calculate(x, y);
}
};
template <typename FpType>
struct AddFormula {
static FpType calculate (FpType x, FpType y) { return x + y; }
};
// composition:
using TheFormulaUser = FormulaUser<AddFormula, float>;
Это нормально, но не так, когда формуле необходимо определить параметры перед передачей в FormulaUser
, Например, LinearFormula
(давайте проигнорируем тот факт, что типы с плавающей точкой не могут быть параметрами шаблона):
template <float A, float B, float C>
struct LinearFormula {
template <typename FpType>
struct Formula {
static FpType calculate (FpType x, FpType y) { return A + B*x + C*y; }
};
};
// composition:
using TheFormulaUser = FormulaUser<LinearFormula<1.0, 2.0, 3.0>::template Formula, float>;
Что мне не нравится в этом коде:
::template Formula part
).LinearFormula
отступ в два раза.Есть ли способ сделать его лучше?
ОБНОВИТЬ
Причина, по которой я хочу разделить параметры формулы на два уровня (константы формул и FpType), заключается в том, что параметры первого набора являются частью пользовательской конфигурации, а параметры второго набора предоставляются классом, который использует Формула. Что ж, FpType также становится пользовательской конфигурацией, но он должен быть одинаковым для всех формул. Эта более сложная композиция демонстрирует это …
using MyProgram = Program<
float, // FpType
AddFormula, // Formula for something
LinearFormula<2.0, 5.3, 5.3>, // Formula for something else
QuadraticFormula<.....>, // For something else...
ExponentialAveraging< // AveragingType
0.6 // SmoothingFactor
>
>;
Таким образом, вы предоставляете FpType корневому классу, и он распространяется на все остальное.
Эти примеры являются искусственными, но они должны объяснить проблему. Я не хочу, чтобы конфигурация содержала больше шаблонов, чем необходимо (в частности, константы с плавающей точкой не могут быть определены, как указано выше …).
Наконец, необходимо использовать метапрограммирование шаблона (как по соображениям производительности, так и в соответствии с существующим кодом).
Я понимаю, что ваш пример несколько надуманный, но я не вижу причин для параметров шаблона и вложенности. Поэтому я предлагаю удалить их и выполнить всю параметризацию типов параметров на уровне формулы. Это создает «аккуратное» и менее вложенное решение, но, возможно, не то, которое вам пригодится. Также обратите внимание, что я переместил параметры шаблона с плавающей запятой в класс политики.
template <class Formula>
struct FormulaUser
{
using param_type = typename Formula::param_type;
param_type some_function()
{
param_type x = 1;
param_type y = 2;
return Formula::calculate(x, y);
}
};
template <typename FpType>
struct AddFormula
{
using param_type = FpType;
static FpType calculate(FpType x, FpType y) { return x + y; }
};
using TheFormulaUser = FormulaUser<AddFormula<float>>;
struct SomeCoefficients
{
static float A()
{
return 1.0;
}
static float B()
{
return 2.0;
}
static float C()
{
return 3.0;
}
};
template <typename FPType, typename LinearPolicy>
struct LinearFormula
{
using param_type = FPType;
static FPType calculate(FPType x, FPType y)
{
return LinearPolicy::A() + LinearPolicy::B() * x + LinearPolicy::C() * y;
}
};
// composition:
using TheFormulaUser2 = FormulaUser<LinearFormula<float, SomeCoefficients>>;
Других решений пока нет …