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; } 
  • one
    Familiar code, however . - αλεχολυτ
  • one
    And why every question to start a new user? - αλεχολυτ
  • @alexolut Yes, but there it was a member function, and now as a free function (called the operator a function, so as not to bother). - StateItPrimitive
  • @StateItPrimitive I have a link to my answer, and not to the question of the TS :) so there is already a free function in the code. Although not sure if you can call the function свободной when it is only due to ADL. - αλεχολυτ
  • @alexolut, if a duplicate, then for both a duplicate and close. And so, the reasons for closing are not clear. - Qwertiy

2 answers 2

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; } 

Compiling with gcc 5.1

    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. - αλεχολυτ