1

I am writing a small library for working with polynomials.

The main class is called poly and it contains 4 overloaded constructors.

An object of class poly is a representation of one polynomial.

Full code can be viewed here: https://github.com/IQ8QI/polynomial-lib

In ./test-poly/test-all.cpp I am trying to create an object of class poly:

poly::poly my_poly = poly::poly((double)6.0, -2.0, 4.0); 

Constructors of class poly are:

//Create polynomial using just numbers poly(double values...); //Create polynomial using vector<double> poly(vector<double> values); //Create polynomial with non-zero base poly(int base, double values...); //Create polynomial with non-zero base and vector poly(int base, vector<double> values); 

Unfortunately, I am receiving a compilation error:

./test-all.cpp:20:63: error: call of overloaded ‘poly(double, double, double)’ is ambiguous 20 | poly::poly my_poly = poly::poly((double)6.0, -2.0, 4.0); | ^ In file included from ./test-all.cpp:3: ././../polynomial-lib/polynomial.hpp:22:17: note: candidate: ‘poly::poly::poly(int, double, ...)’ 22 | poly(int base, double values...); | ^~~~ ././../polynomial-lib/polynomial.hpp:16:17: note: candidate: ‘poly::poly::poly(double, ...)’ 16 | poly(double values...); | ^~~~ 

I understand that the compiler is unable to determine which constructor to use.

I want to stay with solution that there are 4 constructors poly().

To resolve the issue, I can change 2 of the constructors into builder functions, but I don't like it:

//Create polynomial with non-zero base poly special_poly(int base, double values...){ poly temp_poly = poly(values...); temp_poly.set_base(base); return temp_poly; } //Create polynomial with non-zero base and vector poly special_poly(int base, vector<double> values){ poly temp_poly = poly(values); temp_poly.set_base(base); return temp_poly; } 

Can it be done without builder functions?

11
  • please include a minimal reproducible example. Commented Jan 11, 2024 at 8:24
  • 9
    dont use c varargs in c++. c++ has typesafe variadic templates, that avoid this and many other issues Commented Jan 11, 2024 at 8:26
  • 1
    Don't use poly(double values...) instead use variadic templates. Is there a case where vararg functions should be preferred over variadic templates? Commented Jan 11, 2024 at 8:26
  • 2
    but why do you need a vararg constructor in the first place? You already have one that uses a vector. Simply remove poly(int base, double values...); and poly(double values...); Commented Jan 11, 2024 at 8:26
  • perhaps you actually want one single constructor poly(std::initializer_list<double> values,int base = 0) Commented Jan 11, 2024 at 8:29

2 Answers 2

6

You do not need c varargs when you can use c++ variadic templates which are typesafe. However, you already have a constructor that takes a std::vector, so I see no need for more than a single constructor:

#include <vector> struct poly{ std::vector<double> coeffs; int base = 0; poly(std::vector<double> c,int b = 0) : coeffs(std::move(c)),base(b) {} }; int main() { poly p1{{1.0,2.0,3.0}}; poly p2{{1.0,2.0,3.0},0}; } 

I stayed with std::vector passed by value because thats what you asked for. Though, if you never actually need to pass an already constructed vector, but just a list of doubles to the constructor you can change the argument to be a std::initializer_list<double>. If you stay with std::vector consider to pass it as rvalue reference (see below comment by Jan Schultke).

Sign up to request clarification or add additional context in comments.

2 Comments

You'd be better off passing the std::vector by rvalue reference and move-constructing coeffs in accordance with CppCoreGuidelines, or at least passing it by value.
@JanSchultke good point. My intention was to not change too much on OPs code, but then I changed it to the worse. I made it pass by value again and refered to your comment
2

A C++17 compatible solution would look like this (demo https://onlinegdb.com/_ToNa_7J0):

#include <type_traits> #include <iostream> // with a template you can specify multiple arguments (a pack). // the enable_if_t checks that all the individual arguments are of type double (nice bonus : no implicit conversion from int/float to double) template<typename... args_t> auto f(args_t&&... args) -> std::enable_if_t<(std::is_same_v<double,args_t> && ...),void> { std::cout << "All doubles\n"; } template<typename... args_t> auto f(int arg, args_t&&... args) -> std::enable_if_t<(std::is_same_v<double,args_t> && ...),void> { std::cout << "First one is an int the rest is all doubles\n"; } int main() { f(1.0,2.0,3.0); f(1,2.0,3.0); // f(1,2.f,3.0); // This one should not compile, there is a float argument in there return 0; } 

And outputs :

All doubles First one is an int the rest is all doubles 

2 Comments

This is great solution, this is what I asked for. But after seeing the call: ``` f(1.0,2.0,3.0); f(1,2.0,3.0); ``` I consider it easy to make mistake and I will use other solution of 463035818_is_not_an_ai Doing this is a little less error prone: ``` poly p1{{1.0,2.0,3.0}}; poly p2{{1.0,2.0,3.0},0}; ```
That's fine. I usually kind of avoid initializer lists. template<std::size_t> f(double (&values)[N]) and template<std::size_t N> f(int value,double (&values[N]) syntax would give you the same calling semantics but then without initializer lists. But both would still allow for implicit conversion to doubles.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.