#c #templates #constexpr
#c #шаблоны #constexpr
Вопрос:
Я пытаюсь понять, каков наилучший способ обработки шаблонного класса со многими параметрами, отличными от typename.
Чего я хочу избежать, так это чего-то вроде:
template <int parameter1,
int parameter2,
int parameter3,
...
int parameterN>
class Foo {
void bar();
};
template <int parameter1, int parameter2, int parameter3, ..., parameterN>
void Foo::bar() {
// a function of parameter1, parameter2, ..., parameterN
}
Одним из возможных решений является упаковка constexpr внутри структуры, например:
struct ParametersSetA {
static constexpr int parameter1 = ...;
...
static constexpr int parameterN = ...;
};
template <typename ParametersSet>
class Foo {
void bar();
};
template <typename ParametersSet>
void Foo::bar() {
// a function of ParametersSet::parameter1, ..., ParametersSet::parameterN
}
Комментарии:
1. Что вы подразумеваете под «обработкой» их? Какую «обработку» вам нужно выполнить?
2. Что вы имеете в виду
a function of parameter1, parameter2, ..., parameterN
? Anint
не является типом функции.3. Под этим я подразумеваю разработку класса с множеством параметров, отличных от typename, который можно поддерживать и, в идеале, является элегантным решением.
4. Под функцией parameter1, …, parameterN я подразумеваю функцию, которая использует эти постоянные значения параметров int для выполнения операции. Например: ` int bar(arg1, arg2, arg3){ возвращает arg1 * параметр1 arg2 * параметр2 arg3 * параметр3 } `
5. Честно говоря, я всегда просто использовал макрос для этого :(.
#define MY_CLASS_TEMPLATE template <int parameter1, int parameter2, int parameter3, ..., parameterN>
и затемMY_CLASS_TEMPLATE void Foo::bar() {...}
Ответ №1:
Это довольно субъективный вопрос (определите «лучший»). IMO, использование структуры было бы идеальным. Но использование структур в качестве параметров шаблона поддерживается только в C 20.
struct Config {
int a;
int b;
int c;
};
template <Config config>
struct Foo {
int bar(int a, int b, int c) {
return config.A*a config.B*b config.c*c;
}
};
/* ... */
Foo<Config {1, 2, 3}> foo;
Я видел параметры, скрытые с помощью типа:
template <int A, int B, int C>
struct ConfigPack {};
template <typename Config>
struct Foo {
int bar(int a, int b, int c);
};
/* ... */
using Config = ConfigPack<1, 2, 3>;
Foo<Config> foo;
А затем либо перегрузка, либо частичная специализация, используемая для тех методов, которым действительно нужны параметры:
template <int A, int B, int C>
int multiply_accumulate(int a, int b, int c, ConfigPack<A, B, C>) {
return A*a B*b C*c;
}
template <typename Config>
int Foo::bar(int a, int b, int c) {
return multiply_accumulate(a, b, c, Config{});
}
Для вашего надуманного примера, когда все параметры имеют один и тот же тип и объединены шаблонным способом, вы можете использовать вариационный шаблон для упрощения формулы (или, в зависимости от вашей точки зрения, сделать ее ужасно более сложной, чем нужно):
template <int... Is>
class Foo {
// using C 17 fold expressions
void bar(decltype(Is)... args) {
return ((Is * args) ...);
}
// a bit less elegant, but works in C 11
void bar(decltype(Is)... args) {
auto terms = { (Is * args)... };
return std::accumulate(terms);
}
};