Использование переменной числа аргументов float
Я пытаюсь реализовать гибкий конструктор для моей структуры Polynomial:
struct Polynomial
{
std::vector<float> coefficients;
size_t degree;
};
Степень полинома является переменной.
Я хотел бы иметь конструктор типа
Polynomial(float... _coefficients);
Я пробовал вариационный шаблон:
template<float... Args>
Polynomial(Args... args);
Но float не является типом, поэтому я сделал:
template<typename... Args>
Polynomial(Args... args);
Но это позволяет моим коэффициентам быть чем угодно, а не тем, что я хочу.
Я знаю, что могу использовать:
Polynomial(size_t _degree, ...);
Но это действительно опасно.
В данный момент я использую:
Polynomial(std::vector<float>);
Но это заставляет вызов выглядеть как:
Polynomial P({f1, f2, f3}); // with fn floats
Итак, я хотел бы знать, есть ли хороший способ сделать это.
Спасибо!
Ответы
Ответ 1
Я думаю, что ваш путь (векторный параметр или лучше (IMHO) список инициализаторов) является хорошим способом.
Другим способом (простым, но с недостатками) может быть использование сужения, чтобы быть уверенным, что Args...
float
или типы, которые можно сузить до float
. Что-то вроде
struct Polinomial
{
std::vector<double> v;
std::size_t degree;
template <typename ... Args>
Polinomial (Args const & ... args)
: v { float{args}... }, degree { sizeof...(Args) }
{ }
};
Это просто и работает, на примере
Polinomial p { 2.3f, 3.5f, 6.7f };
но ваш конструктор не принимает, например, значения integer или double
или long double
; так
Polinomial p { 2.3f, 3.5f, 6.7 };
// ........................^^^ double, error
Polinomial p { 2.3f, 3.5f, 6 };
// ........................^ int, error
и, вероятно, слишком ограничительный.
Ответ 2
Вы можете использовать initializer_list
:
#include <vector>
#include <initializer_list>
struct Polynomial {
std::vector<float> coeffs;
std::size_t degree;
Polynomial(std::initializer_list<float> init)
: coeffs{ init }, degree(init.size()) { }
};
int main() {
Polynomial p{ 1, 2, 3. };
}
Ответ 3
Отвечая на ваш вопрос
Я хотел бы знать, есть ли хороший способ сделать это
Да, я думаю, что вы делаете это более чем приемлемо. И даже синтаксис, в котором вы его используете, Polynomial P({f1, f2, f3});
не так уродлив.
Кроме того, использование std::vector
столь же эффективно, как и вариационное и гораздо более понятное для других.
С помощью вариационного подхода вам трудно заставить те типы, которые были получены float
, но с std::vector
вы получили контроль над ним
Ответ 4
Вы можете использовать рекурсивную обработку параметров шаблона. Общая идея заключается в использовании частного метода, который добавляет первый параметр в векторы coefficient
и рекурсирует с другими параметрами до тех пор, пока все они не будут обработаны:
struct Polynomial
{
template<class...Args>
Polynomial(Args... coeffs) {
init(coeffs...);
degree = coefficients.size() - 1;
}
std::vector<float> coefficients;
size_t degree;
private:
void init(float coeff) {
coefficients.push_back(coeff);
}
template<class...Args> void init(float first, Args...others) {
init(first);
init(others...);
}
};
Ответ 5
У вас много вариантов. Вы можете посмотреть на конструкторы std::vector
для вдохновения. Например, шаблонный конструктор, который принимает два итератора, очень гибкий:
template<typename T>
Polynomial(T begin, T end) : coefficients(begin, end), degree(coefficients.size()) {}
auto init = std::vector<float>{2.0, 1.0, 4.0};
Polynomial p2{init.begin(), init.end()};
Или вы можете взять std::initializer_list<float>
, как предположил Джодокус.
У вас может быть шаблонный конструктор, который принимает любой тип контейнера:
template<typename T>
Polynomial(T container)
: coefficients(begin(container), end(container))
, degree(coefficients.size()) {}
auto init = std::vector<float>{2.0, 1.0, 4.0};
Polynomial p2{init};
Живая демонстрация.
Или вы можете предоставить комбинацию различных конструкторов для удовлетворения различных потребностей.
Ответ 6
То, как вы это делаете, - "хороший способ". Представьте, что вызывающий абонент хочет передать 100 коэффициентов. Если вы используете переменные, вы вынуждаете вызывающего абонента делать что-то вроде этого:
float c1,c2,c3,....c100;
// init them
Polynomial P(c1, c2, c3,....,c100);
Если бы я использовал 100 коэффициентов, я бы, конечно, сохранил их в векторе, и было бы довольно громоздким передать их вашему полиному:
auto coeffs = std::vector<float>(100);
Polynomial P(coeffs[0],coeffs[1],....,coeffs[100]);
Если, однако, вы принимаете вектор, вызывающий может обойтись без боли:
Polynomial P({c1,c2,c2});
Polynomial P(coeffs);
С другой стороны, использование std::vector
, но не допускающее использование другого контейнера, является произвольным ограничением. Лучше было бы принять итераторы и позволить вызывающему:
Polynomial P(coeffs.begin(),coeffs.end());