Концепт и дружественные операторы класса
Делал одну студенческую работу, вытащил делавшийся в свое время старый класс для работы с полиномами. Сам не люблю простыни, но ужать код оказалось сложно, простите уж... все работает на ура.
Решил выпендриться, и добавить концепты для ограничения "математическими" типами. Написал было
template<typename T>
requires is_integral_v<T>||is_floating_point_v<T>
class Polynome
Опять же, все работает... Но если работать с комплексными числами, ограничения не соблюдаются. Решил, недолго мудрствуя, написать простое ограничение с использованием нужных мне арифметических операций.
template<typename T, typename V>
concept PolyAble = requires(T a, V b){ a + b; a * b; };
template<typename T>
requires PolyAble<T,T>
class Polynome
И все бы ничего — если не писать умножение. Если его написать, то сразу все слетает на реализации дружественных операторов
Polynome<std::common_type_t<U,V>> operator*(const Polynome<U>& a, const V& x)
Polynome<std::common_type_t<U,V>> operator*(const V& x, const Polynome<U>& a)
В обоих случаях ругань в конечном счете доходит до того, что, мол, не получается просчитать PolyAble<Polynome<double>,Polynome<double>>
.
Вот и вопрос: во-первых, не понимаю, почему он вообще выходит на эти типы, а во-вторых, как правильно указывать (если это вообще нужно в такой ситуации) требования к шаблонным свободным функциям — мне казалось, что раз они используют основной класс с ограничителем, то там он и сработает.
Вот вся простынь. Компилятор Visual C++ 2022, 17.6.0.
template<typename T>
concept PolyAble = requires(T a, T b){ a+b; a*b; };
template<typename T>
//requires is_integral_v<T>||is_floating_point_v<T>
requires PolyAble<T>
class Polynome
{
public:
Polynome(size_t n = 0):a(n+1,0){}
Polynome(std::initializer_list<T> cfs)
{
a.insert(a.end(),rbegin(cfs),rend(cfs));
norm();
}
~Polynome() {}
Polynome(const Polynome<T>&) = default;
Polynome& operator=(const Polynome<T>&) = default;
Polynome(Polynome<T>&&) = default;
Polynome& operator=(Polynome<T>&&) = default;
size_t extent() { return a.size()-1; }
T operator[](size_t i) const { return (i >= a.size()) ? T(0) : a[i]; }
std::vector<T> coeffs() const { return a; }
template<typename U>
auto operator()(const U& x) -> std::common_type_t<U,T>
{
std::common_type_t<U,T> res = std::common_type_t<U,T>();
for(auto c = a.rbegin(); c != a.rend(); ++c)
{
res = res*x + *c;
}
return res;
}
template<typename U, typename V>
friend Polynome<std::common_type_t<U,V>> operator+(const Polynome<U>& a, const Polynome<V>& b);
template<typename U, typename V>
friend Polynome<std::common_type_t<U,V>> operator-(const Polynome<U>& a, const Polynome<V>& b);
template<typename U, typename V>
friend Polynome<std::common_type_t<U,V>> operator*(const Polynome<U>& a, const V& x);
template<typename U, typename V>
friend Polynome<std::common_type_t<U,V>> operator*(const V& x, const Polynome<U>& a);
template<typename U, typename V>
friend Polynome<std::common_type_t<U,V>> operator*(const Polynome<U>& p, const Polynome<V>& q);
template<typename U>
friend std::ostream& operator<<(std::ostream&,const Polynome<U>&);
private:
std::vector<T> a;
void norm() {
for(auto i = a.rbegin(); i != a.rend(); i = a.rbegin())
if (*i == T(0)) a.erase(i.base()-1); else break;
if (a.empty()) a.push_back(T{});
}
};
template<typename T>
std::ostream& operator<<(std::ostream&os,const Polynome<T>&p)
{
os << "(";
for(auto i = p.a.rbegin(); i != p.a.rend(); ) { os << *i; if (++i != p.a.rend()) os << ","; }
return os << ")";
}
template<typename U, typename V>
Polynome<std::common_type_t<U,V>> operator+(const Polynome<U>& a, const Polynome<V>& b)
{
size_t N = std::max(a.a.size(),b.a.size());
Polynome<std::common_type_t<U,V>> p(N-1);
for(size_t i = 0; i < N; ++i) p.a[i] = a[i] + b[i];
p.norm();
return p;
}
template<typename U, typename V>
Polynome<std::common_type_t<U,V>> operator-(const Polynome<U>& a, const Polynome<V>& b)
{
size_t N = std::max(a.a.size(),b.a.size());
Polynome<std::common_type_t<U,V>> p(N-1);
for(size_t i = 0; i < N; ++i) p.a[i] = a[i] - b[i];
for(auto i = p.a.rbegin(); i != p.a.rend(); i = p.a.rbegin())
if (*i == std::common_type_t<U,V>{}) p.a.erase(i.base()-1); else break;
p.norm();
return p;
}
template<typename U, typename V>
Polynome<std::common_type_t<U,V>> operator*(const Polynome<U>& a, const V& x)
{
Polynome<std::common_type_t<U,V>> p(a.a.size()-1);
for(size_t i = 0; i < a.a.size(); ++i) p.a[i] = a.a[i]*x;
p.norm();
return p;
}
template<typename U, typename V>
Polynome<std::common_type_t<U,V>> operator*(const V& x, const Polynome<U>& a)
{
return a*x;
}
template<typename U, typename V>
Polynome<std::common_type_t<U,V>> operator*(const Polynome<U>& p, const Polynome<V>& q)
{
Polynome<std::common_type_t<U,V>> r(p.a.size()+q.a.size()-2);
for(size_t i = 0; i < p.a.size(); ++i)
for(size_t j = 0; j < q.a.size(); ++j) r.a[i+j] += p[i]*q[j];
r.norm();
return r;
}
using Poly = Polynome<double>;
int main(int argc, char * argv[])
{
double x[5] = {3,4,5,6,7};
double y[5];
Poly org{1.,2.,3.,0.,0.,0.,-3.};
for(int i = 0; i < 5; ++i) y[i] = org(x[i]);
vector<Poly> L(5,Poly{1});
for(int i = 0; i < 5; ++i)
{
for(int j = 0; j < 5; ++j)
if (i != j)
L[i] = L[i]*Poly{1,-x[j]}*(1.0/(x[i]-x[j]));
cout << i << ": " << L[i] << endl;
}
}