Search code examples
c++templatesoperator-overloadingimplicit-conversiontemplate-argument-deduction

Scenarios where template deduction requires user-defined implicit conversion


I have a class template that includes an overloaded operator template and an implicit conversion constructor, similar to the following:

#include <utility>

template <class A, class B>
class X {
public:
    A a;
    B b;

    X(A a, B b) : a(a), b(b) {}
    X(std::pair<A, B> &value) : a(value.first), b(value.second) {}

    template <class C>
    friend X<A, C> operator+(const X<A, B> &left, const X<B, C> &right) {
        return X<A, C>(left.a, right.b);
    }
};

int main() {
    X<int, int> a(1, 2);
    X<int, float> b(1, 2.5f);
    std::pair<int, float> c(1, 2.5f);

    a + b; // Valid
    a + c; // Template deduction error
}

I found this answer on a different question, but I don't see any way to implement that solution here. (Note that it is important that the second template parameter of the first argument and the first template parameter of the second argument are the same, the actual operator implementation is not as trivial, but the interface is the same.


Solution

  • @R-Sahu and the link you cite have already answered your question. But a solution would be to provide another operator+ for types that are not X that does the conversion after the deduction for C,

    #include <utility>
    
    template <class A, class B>
    class X {
    public:
        A a;
        B b;
    
        X(A a, B b) : a(a), b(b) {}
        X(const std::pair<A, B> &value) : a(value.first), b(value.second) {}
    
    
        template <class C,template <typename, typename> typename O >
        friend X<A, C> operator+(const X<A, B> &left, const O<B, C> &right) {
           return left + X<B,C>(right);
        }
        template <class C>
        friend X<A, C> operator+(const X<A, B> &left, const X<B, C> &right) {
           return X<A, C>(left.a,right.b);
        }
    };
    
    
    int main() {
        X<int, int> a(1, 2);
        X<int, float> b(1, 2.5f);
        std::pair<int, float> c(1, 2.5f);
    
        a + b; // Valid
        a + c; // Works
    }
    

    Demo