Search code examples
c++namespacesoperator-overloadingoverload-resolution

Overload resolution for template operators in namespaces


I ran into a bit of trouble with template operator overloads when entering namespaces. Consider addition of arrays:

// overloads.hpp
#include <array>

namespace mylib {

template <size_t N>
using DoubleArray = std::array<double,N>;

template <size_t N>
DoubleArray<N> operator+( const DoubleArray<N>& lhs, const DoubleArray<N>& rhs ) {return DoubleArray<N>();}

}

Testing this in namespace mylib works as intended.

// test.cpp
#include "overloads.hpp"

namespace mylib {

void test()
{
    DoubleArray<3> a({1.0,0.0,0.0});
    DoubleArray<3> b({0.0,1.0,0.0});
    DoubleArray<3> c(a+b);                        // <-- ok
}

}

Now suppose that I have a Complex class in namespace mylib::mysublib that has its own operator+ and a constructor from DoubleArray (this constructor has to be explicit to prevent implicit conversion):

// nested.cpp
#include "overloads.hpp"

namespace mylib {
    namespace mysublib {

        struct Complex
        {
            Complex() {};
            explicit Complex( const DoubleArray<2>& components );

            DoubleArray<2> _components;
        };

        Complex operator+(const Complex& rhs, const Complex& lhs) {return Complex();}



        void testNested()
        {
            DoubleArray<2> a({1.0,0.0});
            DoubleArray<2> b({0.0,1.0});
            DoubleArray<2> c(a+b);                        // <-- no match for ‘operator+’
            DoubleArray<2> d( mylib::operator+(a,b) );    // <-- ok
        }

    }
}

Error message:

error: no match for ‘operator+’ (operand types are ‘mylib::DoubleArray<2> {aka std::array<double, 2>}’ and ‘mylib::DoubleArray<2> {aka std::array<double, 2>}’)
     DoubleArray<2> c(a+b);                        // <-- no match for ‘operator+’

Why can't the overloaded operator be found when called from the nested namespace? The whole point of overloading (in this example) would be a clean syntax. Any ideas on how to get this working, or if it's even possible?


Solution

  • The operator+ of Complex can be declared as friend function in Complex, which does not pollute the global namespace. Your example should compile after the following change.

    struct Complex {
      Complex(){};
      explicit Complex(const DoubleArray<2>& components);
    
      DoubleArray<2> _components;
    
      friend Complex operator+(const Complex& rhs, const Complex& lhs) { return Complex(); }
    };
    

    According to C++ standard working draft N4140,

    When two or more different declarations are specified for a single name in the same scope, that name is said to be overloaded.

    In your case, the two operator+ functions are declared in different namespace and thus are not qualified for overload resolution.

    When compiler finds the first match Complex operator+(const Complex& rhs, const Complex& lhs), DoubleArray cannot be implicitly converted to Complex. Therefore, you got the no match for ‘operator+’ error.