Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Operator overload not found when used in a lambda within a namespace

The following does not compile (with Clang 5.0.0 / gcc 7.3, std: C++11):

Error message in Clang:

error: invalid operands to binary expression (std::vector<double, std::allocator<double> > and std::vector<double, std::allocator<double>>)

#include <functional>
#include <vector>

namespace ns{

using MyType = std::vector<double>;

} // namespace ns

using ns::MyType;
MyType& operator+=( MyType& lhs, const MyType& rhs) {
  for (int i = 0; i < lhs.size(); ++i) {
    lhs[i] = lhs[i] + rhs[i];
  }
  return lhs;
}
MyType operator+( MyType lhs, const MyType& rhs) {
  lhs += rhs;
  return lhs;
}

namespace ns{

using Func = std::function<MyType()>;

Func operator+(
    const Func &lhs, const Func &rhs) {
  return [lhs, rhs]() {
    auto y = lhs() + rhs(); // <-- error in this line
    return y;
  };
}

} // namespace ns

The compiler does not find the correct overload of operator+. I do not understand why. The reason I define the operators outside the namespace is that ADL does not work for typedefs and using type aliases. What is the problem here? Why can't the compiler find operator+(MyType, const MyType &) in the above circumstances?

All of the following alternatives do compile:

namespace ns {

MyType a, b;
auto c = a + b; // compiles

MyType f() {
    MyType a_, b_;
    return a_ + b_; // compiles
};

Func operator+(
    const Func &lhs, const Func &rhs) {
  return [lhs, rhs]() {
    auto x = lhs();
    x += rhs(); // <-- compiles; operator+= instead of operator+
    return x;
  };
}

} // namespace ns

Func operator+(
    const Func &lhs, const Func &rhs) {
  return [lhs, rhs]() {
    auto y = lhs() + rhs(); // <-- no error if not in namespace ns
    return y;
  };
}
like image 806
user2296653 Avatar asked Mar 07 '23 11:03

user2296653


1 Answers

You are hiding the global namespace operator+ because you have defined an operator+ in the current scope. Unqualified name lookup keeps moving to the enclosing namespace until it has found at least one declaration. So because there is an operator+ in the current namespace, regardless of its argument list, name lookup does not proceed to search in the global namespace. See here for a detailed explanation of qualified lookup. The relevant bit is at the top.

For an unqualified name, that is a name that does not appear to the right of a scope resolution operator ::, name lookup examines the scopes as described below, until it finds at least one declaration of any kind, at which time the lookup stops and no further scopes are examined.

Note how both of these examples work. Only the changed segment is shown, the rest of your code must still be visible in order to compile.

Explicitly include ::operator++ in unqualified name lookup

namespace ns {
    using Func = std::function<MyType()>;
    using ::operator+;

    Func operator+(
        const Func &lhs, const Func &rhs) {
        return [lhs, rhs]() {
            auto y = lhs() + rhs();
            return y;
        };
    }

} // namespace ns

Make sure no function is hiding global operator+

namespace ns {
    using Func = std::function<MyType()>;

    Func func(
        const Func &lhs, const Func &rhs) {
        return [lhs, rhs]() {
            auto y = lhs() + rhs();
            return y;
        };
    }

} // namespace ns
like image 100
patatahooligan Avatar answered Apr 06 '23 06:04

patatahooligan