Can refactoring an overloaded operator into a non-member function break any code?

后端 未结 1 1462

Consider a legacy class template with overloaded addition operators += and +

template
class X
{
public:
    X() = de         


        
相关标签:
1条回答
  • 2021-01-12 00:41

    Summary

    The answer is, yes, there will always be breakage. The essential ingredient is that function template argument deduction does not consider implicit conversions. We consider three scenarios, covering the three syntactic forms that an overloaded operator can take.

    Here we use an implicit constructor inside X<T> itself. But even if we made that constructor explicit, users could add to the namespace of X<T> a class C<T> that contains an implicit conversion of the form operator X<T>() const. The scenarios below would continue to hold in that case.

    A non-member friend function breaks the least in the sense that it will allow lhs argument implicit conversions that would not compile for a class template's member function. The non-member function template breaks the implicit conversion on rhs arguments.

    Class template's member function

    template<class T>
    class X
    {
    public:
        /* implicit */ X(T val) { /* bla */ }
    //...
        X<T> operator+(X<T> const& rhs) { /* bla */ }
    //...
    };
    

    This code will allow expression like

    T t;
    X<T> x;
    x + t;  // OK, implicit conversion on non-deduced rhs
    t + x;  // ERROR, no implicit conversion on deduced this pointer
    

    Non-member friend function

    template<class T>
    class X
    {
    public:
        /* implicit */ X(T val) { /* bla */ }
    //...
        friend 
        X<T> operator+(X<T> const& lhs, X<T> const& rhs) { /* bla */ }
    //...
    };
    

    Since the friend function is a not a template, no argument deduction takes place and both the lhs and rhs argument consider implicit conversions

    T t;
    X<T> x;
    x + t;  // OK, implicit conversion on rhs
    t + x;  // OK, implicit conversion on lhs
    

    Non-member function template

    template<class T>
    class X
    {
    public:
        /* implicit */ X(T val) { /* bla */ }
    //...
    };
    
    template<class T> 
    X<T> operator+(X<T> const& lhs, X<T> const& rhs) { /* bla */ }
    

    In this case, both the lhs and rhs arguments undergo argument deduction, and neither takes implicit conversions into account:

    T t;
    X<T> x;
    x + t;  // ERROR, no implicit conversion on rhs
    t + x;  // ERROR, no implicit conversion on lhs
    
    0 讨论(0)
提交回复
热议问题