在名称空间内的 lambda 中使用时找不到运算符重载

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

以下不编译(使用 Clang 5.0.0 / gcc 7.3,std:C++11):

Clang 中的错误消息:

错误:二进制表达式的操作数无效(std::vector<double, std::allocator<double> >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

编译器找不到 operator+ 的正确重载。我不懂为什么。我在命名空间外定义运算符的原因是 ADL does not work for typedefs and using type aliases。这里有什么问题?为什么编译器在上述情况下找不到operator+(MyType, const MyType &)

以下所有备选方案都可以编译:

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;
  };
}

您正在隐藏全局命名空间 operator+,因为您在当前范围内定义了一个 operator+。非限定名称查找一直移动到封闭的名称空间,直到找到至少一个声明。因此,因为当前命名空间中有一个 operator+,无论其参数列表如何,名称查找都不会继续在全局命名空间中进行搜索。有关限定查找的详细说明,请参阅 here。相关位在顶部。

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.

请注意这两个示例的工作原理。仅显示更改的段,其余代码必须仍然可见才能编译。

在非限定名称查找中明确包含 ::operator++

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

确保没有函数隐藏全局 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

这是因为 ADL 不会自动搜索封闭的命名空间,除非它是内联命名空间:

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.

考虑以下代码:

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.
}

同样,在您的情况下,关联的命名空间是 std,ADL 不会考虑封闭的全局命名空间。

其实你代码中的operator+是通过不合格的名字查找找到的,只要找到一个名字就会停止。当在namespace ns中声明operator+时,首先找到该运算符,不会搜索全局命名空间中的运算符。