I'm trying to implement a flexible constructor for my struct Polynomial :
struct Polynomial
{
std::vector<float> coefficients;
size_t degree;
};
The degree of the polynomial is variable. What I would like is to have a constructor like
Polynomial(float... _coefficients);
I've tried variadic template :
template<float... Args>
Polynomial(Args... args);
But float is a non-type, so I've done :
template<typename... Args>
Polynomial(Args... args);
But this allow my coefficients to be anything, not realy what I want. I know I could use :
Polynomial(size_t _degree, ...);
But it is really unsafe.
At the moment I'm using :
Polynomial(std::vector<float>);
But this force the call to be like :
Polynomial P({f1, f2, f3}); // with fn floats
So I would like to know if there is a good way to do this.
Thank you !
You can use 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. };
}
Answering your question
I would like to know if there is a good way to do this
Yes, i think the way you are doing it it's more than acceptable. And even the syntax where you use it Polynomial P({f1, f2, f3});
isn't so ugly at all.
Furthermore, use std::vector
is as efficient as variadic and much more comprehensible for others.
With the variadic approach, you will find difficult to force those types received to be float
, but with std::vector
you got it controlled
I think that your way (a vector parameter, or better (IMHO) an initializer list) is a good way.
Another way (simple but with drawbacks) could be the use of narrowing to be sure that Args...
are float
or types that can be narrowed to float
. Something like
struct Polinomial
{
std::vector<double> v;
std::size_t degree;
template <typename ... Args>
Polinomial (Args const & ... args)
: v { float{args}... }, degree { sizeof...(Args) }
{ }
};
It's simple and works, by example
Polinomial p { 2.3f, 3.5f, 6.7f };
but your constructor doesn't accept, by example, integer or double
or long double
values; so
Polinomial p { 2.3f, 3.5f, 6.7 };
// ........................^^^ double, error
Polinomial p { 2.3f, 3.5f, 6 };
// ........................^ int, error
and probably is too restrictive.
You can use a recursive template parameter processing. The general idea is to use a private method that adds first parameter to the coefficient
vectors and recurse with other parameters until they have all been processed:
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...);
}
};
You have many options. You might want to look at the constructors of std::vector
for inspiration. For example a templated constructor that takes two iterators is very flexible:
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()};
Or you could take a std::initializer_list<float>
as Jodocus suggested.
You could have a templated constructor that takes any container type:
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};
Or you could provide a combination of different constructors to suit different needs.
The way you are doing it is a "good way". Imagine the caller wants to pass 100 coefficients. If you use variadics you force the caller to do something like that:
float c1,c2,c3,....c100;
// init them
Polynomial P(c1, c2, c3,....,c100);
If I was to use 100 coefficients I would certainly store them in a vector, and it would be rather cumbersome to pass them to your polynomial:
auto coeffs = std::vector<float>(100);
Polynomial P(coeffs[0],coeffs[1],....,coeffs[100]);
If however, you accept a vector, the caller can do both without pain:
Polynomial P({c1,c2,c2});
Polynomial P(coeffs);
On the other hand, using std::vector
but not allowing a different container is an arbitrary restriction. The better way then would be to accept iterators, and let the caller do:
Polynomial P(coeffs.begin(),coeffs.end());
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.