为什么使用命名空间定义的类型实例化的 std::weak_ptr 的重载 operator== 找不到?

8

我正在使用 Visual Studio 2015。

不知道为什么这段代码可以编译:

#include <memory>

class Foo;
class Bar;
typedef std::pair<Foo*,std::weak_ptr<Bar>> Object;
typedef std::vector<Object> ObjectVect;

bool operator==( std::weak_ptr<Bar> left,
                 std::weak_ptr<Bar> right )
{
    return left.lock() == right.lock();
}

int main( int argc, char* argv[] )
{
    ObjectVect vect;
    Object obj;
    auto foundIter = std::find( vect.begin(), vect.end(), obj );
    return 0;
}

而这个则给我报错:

#include <memory>

class Foo;
namespace MyNamespace
{
    class Bar;
}
typedef std::pair<Foo*,std::weak_ptr<MyNamespace::Bar>> Object;
typedef std::vector<Object> ObjectVect;

bool operator==( std::weak_ptr<MyNamespace::Bar> left,
                 std::weak_ptr<MyNamespace::Bar> right )
{
    return left.lock() == right.lock();
}

int main( int argc, char* argv[] )
{
    ObjectVect vect;
    Object obj;
    auto foundIter = std::find( vect.begin(), vect.end(), obj );
    return 0;
}

Error C2678 二进制运算符“==”:没有找到接受左侧操作数类型为“const std::weak_ptr”的运算符(或者没有可接受的转换方式) test_cppunit_interpreter_base_multi_output c:\program files (x86)\microsoft visual studio 14.0\vc\include\utility 216

看起来像是当Bar位于命名空间中时无法找到比较器......

我做错了什么吗?还是这可能是编译器的bug?


2
这可能会有所帮助:https://dev59.com/RHA65IYBdhLWcg3wzyBl#3623643你应该将运算符重载放在与你的类相同的命名空间中。 - pergy
包含类型的命名空间,也应该包括该类型的自由函数。所以回答“我做错了什么吗?”,我会说你在错误地使用命名空间。 - StoryTeller - Unslander Monica
@StoryTeller 免费函数适用于std::weak_ptr<Bar>,但该类型并未包含在namespace MyNamespace中。 - Walter
@StoryTeller 不是的,只有Bar 是。 - Walter
1
@Walter - 老实说,我不知道你在争论什么。第一个例子是正确使用命名空间(通过省略,类型和自由函数都在全局命名空间中)。而第二个则是不正确的命名空间使用。 - StoryTeller - Unslander Monica
显示剩余2条评论
1个回答

12

你应该将operator==移入命名空间以使ADL生效;ADL也会检查用作模板参数的类型(即MyNamespace::Bar)并将相关的命名空间(例如MyNamespace)添加到名称查找的集合中。

namespace MyNamespace
{
    class Bar;
    bool operator==( std::weak_ptr<Bar> left,
                     std::weak_ptr<Bar> right )
    {
        return left.lock() == right.lock();
    }

}

第一个例子为什么能正常工作?

因为ADL(Argument-Dependent Lookup,参数依赖查找)也适用于全局命名空间。在第一个例子中,Baroperator==都定义在同一个命名空间(即全局命名空间)中。

为什么第二个例子不起作用?

首先要注意的是,std::find被定义在命名空间std中,而且在其中定义了许多不同参数类型的operator==。然后根据未限定名称的查找规则,当在命名空间std中找到operator==时,查找就停止了。这意味着,如果没有ADL的帮助,全局命名空间中定义的operator==将根本无法被找到。


3
此情况下无需在 Bar 前加上 MyNamespace:: 前缀。 - user7860670
2
也许你可以解释一下为什么在namespace之外声明的operator==不能使用,即为什么需要ADL。另外,由于std::weak_ptr<>显然属于std而不是MyNamespace,所以ADL是如何起作用的呢? - Walter
1
@jpo38 - 这并不是无效的,只是不要期望未经限定的查找能够工作。 - StoryTeller - Unslander Monica
太好了,加一分! - Walter
这应该提到两阶段查找。 - T.C.
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接