1

I'm trying to overload comparison operators as non-members for a particular templated class sub, 1) between instances of sub, and 2) between sub and a specific variable, which returns an instance of either comparer_sub for the first case and comparer_el in the alternative, which perform comparisons on sub along with some other useful members:

template <typename T1, typename T2>
class sub_base {
public:
    sub_base() {};
};

template <typename T>
class mat {
    class sub : public sub_base<T,mat<T>> {
    public:
        sub(): sub_base<T,mat<T>>() {};
    };
public:
    int mb;
    sub getSub() {return sub();};
};

template <typename T1,typename T2>
class comparer_base {
public:
    comparer_base() {};
    void base_method() {};
};

template <typename lT1,typename lT2,typename rT1,typename rT2>
class comparer_sub :  public comparer_base<sub_base<lT1,lT2>,sub_base<rT1,rT2>> {
    using comparer_base<sub_base<lT1,lT2>,sub_base<rT1,rT2>>::base_method;
public:
    comparer_sub() : comparer_base<sub_base<lT1,lT2>,sub_base<rT1,rT2>>() {};
};

template <typename lT1,typename lT2,typename rT>
class comparer_el :  public comparer_base<sub_base<lT1,lT2>,rT> {
    using comparer_base<sub_base<lT1,lT2>,rT>::base_method;
public:
    comparer_el() : comparer_base<sub_base<lT1,lT2>,rT>() {};
};

template <typename lT1,typename lT2,typename rT1,typename rT2>
comparer_sub<lT1,lT2,rT1,rT2> operator== (const sub_base<lT1,lT2>& lhs, const sub_base<rT1,rT2>& rhs){
    printf("comparer_sub\n");
    return comparer_sub<lT1,lT2,rT1,rT2>();
};
template <typename lT1,typename lT2,typename rT>
comparer_el<lT1,lT2,rT> operator== (const sub_base<lT1,lT2>& lhs, const rT& rhs){
    printf("comparer_el\n");
    return comparer_el<lT1,lT2,rT>();
};

However, any type of comparison I try to perform the second overload is called, what I assume is due to const rt& being too generic and any instance of sub fits that argument.

int main(int argc, char const *argv[])  {
    mat<int>     A;
    mat<float>  B;
    float C = 0.6;

    A.getSub() == B.getSub(); // comparer_el
    A.getSub() == C; // comparer_el

    return 0;
}

How can I force the first overload to be prioritized over the second overload between instances of sub??

joaocandre
  • 1,621
  • 4
  • 25
  • 42
  • @user1810087 I've edited the question, needed to call `getSub()` on comparison types. – joaocandre Nov 13 '18 at 15:30
  • now it does [compile](http://coliru.stacked-crooked.com/a/063184945d2f348c) :) BTW, i did not downvote your question... counter +1, because the question is interesting... – user1810087 Nov 13 '18 at 15:36
  • Cannot put it into good words right now, but the problem is, `comparer_el` with the second parameter beeing substituted from `sub` to `typename rT2` is a better choise as the substitution+cast from `sub` to `sub_base`. **Substitution does not implicitly cast...** So, one solution is not to use return type `sub` with `getSub()` but the base type `sub_base`. see [here, only change line 22](http://coliru.stacked-crooked.com/a/cbe17acb56181f11) – user1810087 Nov 13 '18 at 15:50
  • Also, return type `sub_base` should be prefered as class `sub` is nested and only known inside class mat... – user1810087 Nov 13 '18 at 15:59
  • Fair enough, I think I've understood the problem. The whole reason I nested class `sub` was to simplify the syntax and avoid trailing template arguments (the actual code is quite cluttered and hard to "read" as it is) - would a templated typedef work in this case? – joaocandre Nov 13 '18 at 16:04
  • Also, write an answer and I'll mark it as accepted. – joaocandre Nov 13 '18 at 16:04
  • I actually don't know what you mean by "if a templated typedef would work", except like [this](http://coliru.stacked-crooked.com/a/f6cc6afdb2bcdef1) (only class mat changed). This (only) improves readability. Is `class sub_base` in your actual code a CRTP class? If so, you probalby will have some other issues, too... – user1810087 Nov 13 '18 at 16:38

1 Answers1

1

The problem is, comparer_el with rT=sub is a better choise than the substitution+cast from sub to sub_base<rT1,rT2>. Deduction does not implicitly cast (except putting additional c/v qualifiers)... For a much better description, read this answer.

So, one solution is not to use return type sub within getSub() but the base type sub_base<rT1,rT2>. see here

important part:

template <typename T>
class mat {
    class sub : public sub_base<T,mat<T>> {
    public:
        sub(): sub_base<T,mat<T>>() {};
    };
public:
    int mb;
    sub_base<T,mat<T>> getSub() {return sub();};
 // ^^^^^^^^^^^^^^^^^^ return type changed from sub to sub_base<T,mat<T>>
};
user1810087
  • 5,146
  • 1
  • 41
  • 76