How to make compiler differentiate f(double...) and f(int, double...)

150 views Asked by At

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?

2

There are 2 answers

2
463035818_is_not_an_ai On BEST ANSWER

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).

2
Pepijn Kramer On

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