4

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;
  };
}
5
  • @user463035818 The operator+ I want to use is for MyType, which is the result of calling an instance of Func. The code is lhs() + rhs(), not lhs + rhs. Am I missing something here? Commented Apr 6, 2018 at 9:17
  • 1
    uh no sorry, I was missing something. Please include the error message in the question Commented Apr 6, 2018 at 9:19
  • @JiveDadson Yes: "What is the problem here?", meaning why does this not compile? Why can't the compiler find operator+(MyType, const MyType &) in the above circumstances? I updated the question to clarify this more. Commented Apr 6, 2018 at 9:48
  • Here is an interesting tidbit: This compiles, but adding an unrelated operator+ kills it... Commented Apr 6, 2018 at 9:51
  • 1
    @JiveDadson He obviously wants to know what makes it illegal. And I don't see the problem with the example size, it is complete, verifiable and minimal for all the observations he shares. Commented Apr 6, 2018 at 9:53

2 Answers 2

4

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
Sign up to request clarification or add additional context in comments.

Comments

3

This is because ADL won't automatically search an enclosing namespace, unless it's an inline namespace:

cppreference

If any namespace in the associated set of classes and namespaces is an inline namespace, its enclosing namespace is also added to the set.

Consider the following code:

namespace N {
    namespace M {
        struct foo {};
    }

    void operator+(M::foo, M::foo) {}
}


int main()
{
    N::M::foo f;
    f + f; // Error, enclosing namespace N won't be searched.
}

Similarly, in your case, the associated namespace is std, the enclosing global namespace won't be considered by ADL.

In fact, the operator+ in your code is found by unqualified name lookup, which will stop as long as a name is found. When operator+ is declared in namespace ns, this operator is found first, and the one in global namespace won't be searched.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.