template <class T> struct B; template <class T> struct A { operator B<T>(); }; template <class T> struct B { A<T> operator[](const B&) { return A<T>(); } friend B operator*(const B&, const B&) { return B(); } }; template <class T> A<T>::operator B<T>() { return B<T>(); } int main() { B<int> s1, s2, s3; s1[s2] * s3; s3 * s1[s2]; //s1[s2] * s1[s2];// <-- как сделать так, чтобы это работало (без повторных деклараций *)? s3 * s3; }
2 answers
Relapse Suggestions
In general, it would be worthwhile to render the implementation of a free operator.
friend B operator*(const B&, const B&) { return B(); }
out of class, because it turns out that it is in its namespace (if you can call it that here) and only at the expense of ADL (Argument Dependent Lookup) the compiler selects the appropriate namespace
s1[s2] * s3; s3 * s1[s2];
in these cases due to the second parameter, which, just, is an instance of this class (class B
).
But in this line:
s1[s2] * s1[s2]
Both operator arguments are instances of class A
As a result, the compiler cannot calculate the required namespace due to the arguments of this operator.
But, on the other hand, if we carry out the implementation, then those 2 above lines will no longer compile, since in template functions / classes of implicit type conversion of parameters does not occur.
template <typename T> B<T> operator*(const B<T>& s1, const B<T>& s2){ return B<T>(); } template <class T> struct B { A<T> operator[](const B&) { return A<T>(); } friend B operator* <T>(const B&, const B&); };
Solutions:
1) Use explicit type conversions:
static_cast<B<int>>(s1[s2]); // либо (B<int>)s1[s2]
2) Organize operator*
through the base class ( Base
) and inherit your classes ( A
and B
) from it:
template <class T> struct Base{}; template <class T> struct B; template <class T> struct A: public Base<T> { operator B<T>(); }; // здесь, если понадобится, то закастуете типы // ведь динамический тип параметров остался прежним template <typename T> B<T> operator*(const Base<T>& s1, const Base<T>& s2){ return B<T>(); } template <class T> struct B: public Base<T> { A<T> operator[](const B&) { return A<T>(); } friend B operator* <T>(const Base<T>&, const Base<T>&); }; template <class T> A<T>::operator B<T>(){ return B<T>(); } int main() { B<int> s1, s2, s3; s1[s2] * s3; s1[s2] * s3; s3 * s1[s2]; s1[s2] * s1[s2]; s3 * s3; }
Explicitly cast type A<int>
to B<int>
(both are possible, only one is possible) -
static_cast<B<int>>(s1[s2]) * static_cast<B<int>>(s1[s2]);
Is this option suitable?
- IMHO, this option violates the natural record, which asks the vehicle. - αλεχολυτ
- @alexolut Then you need to add new ads, which he absolutely does not want. - Harry
- Here the vehicle wanted even more than me. I would be satisfied with the option without additional definitions. Although the ads for each type of T, I also do not agree. - αλεχολυτ
свободной
when it is only due to ADL. - αλεχολυτ