C : шаблонный класс со многими параметрами, отличными от typename

#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 ? An int не является типом функции.

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);
    }
};