代码之家  ›  专栏  ›  技术社区  ›  Mohan

对boost::optional使用相等运算符

  •  5
  • Mohan  · 技术社区  · 6 年前

    我试图为在另一个命名空间中定义的类型T定义一个相等运算符,然后在 optional<T> . 在clang(Apple LLVM 9.1.0)上,此代码:

        namespace nsp {
            struct Foo {
            };
        }
        bool operator==(const nsp::Foo& a, const nsp::Foo& b);
    
        void foo() {
            optional<nsp::Foo> a = none;
            optional<nsp::Foo> b = none;
            if (a == b)
                ;
        }
    

    导致错误:

    /usr/local/include/boost/optional/detail/optional_relops.hpp:29:34: error: invalid operands to binary expression ('const nsp::Foo' and 'const nsp::Foo')
    { return bool(x) && bool(y) ? *x == *y : bool(x) == bool(y); }
                          ~~ ^  ~~
    MWE.cpp:40:19: note: in instantiation of function template specialization 'boost::operator==<what3words::engine::nsp::Foo>' requested here
                if (a == b)
                      ^
    /usr/local/include/boost/optional/detail/optional_relops.hpp:28:6: note: candidate template ignored: could not match 'optional<type-parameter-0-0>' against 'const nsp::Foo'
    bool operator == ( optional<T> const& x, optional<T> const& y )
    

    怎么了?我猜这和柯尼格的查找规则有关。。。

    2 回复  |  直到 6 年前
        1
  •  5
  •   Yakk - Adam Nevraumont    6 年前

    立即修复

    执行以下操作:

    namespace nsp {
      bool operator==(const Foo& a, const Foo& b);
    }
    

    来解决你的问题。

    Foo ,您可以改为:

    namespace nsp {
      struct Foo {
        friend bool operator==(const Foo& a, const Foo& b) {
          return true;
        }
      };
    }
    

    如果 是模板类。

    你的解决方案出了什么问题

    这是怎么回事 optional 是在 std (或 boost 或者其他)在这个名称空间中,它试图 nsp::Foo == nsp::Foo 打电话来。

    == 这不适用于 ::std :: ;一旦发现 任何 == 它停止寻找,即使这些论点完全无关。它也在寻找 == ::nsp . 但它从不看进去 ::

    向类型添加运算符时,始终在类型的命名空间中定义运算符。

    可以重新打开命名空间。因此,如果无法控制头文件,可以使用 == 在里面。这个 必须在每个地方都能看到 optional<nsp::Foo>::operator== 由于ODR冲突而被调用或程序格式错误(在本例中,还会生成编译器错误,这有助于避免heizenbug)。

    长版

    首先,它在本地(在本地命名空间中)查找。如果在那里找到什么,搜索就会停止。(这包括 using ns::identifier; 注入到命名空间中的名称,但通常不是 using namespace foo; ). 即使函数或运算符不适用于所讨论的类型,也会发生此“停止”;任何 == 对于命名空间中的任何类型,都将停止此搜索。

    如果找不到匹配项,它将开始查找封闭命名空间,直到找到函数/运算符或到达根命名空间为止。如果有的话 声明中,这些命名空间中的函数/运算符被认为位于 using namespace 正在导入的位置和命名空间。(所以 using namespace std; 在里面 namespace foo 使它看起来像 性病 :: ,不在 foo ).

    接下来,ADL(参数相关查找)完成。将检查所有函数/运算符参数的关联命名空间。此外,还将(递归地)检查模板的所有类型参数的关联命名空间。

    将收集与名称匹配的运算符/函数。对于ADL,不检查父命名空间。

    这两个运算符/函数集合是重载解析的候选对象。

    == 被称为是 促进 . 促进 有很多 == 运算符(即使它们不适用),因此 在里面 都是候选人。

    接下来,我们检查参数的名称空间-- nsp::Foo 在这种情况下。我们进去看看 nsp 看不到 == .

    现在,当您移动用户定义的 == namespace nsp ,然后将其添加到 在ADL步骤中找到。当它匹配时,它被称为。

    超负荷解决(它对候选人做什么)是它自己复杂的主题。简而言之,它试图找到包含最少转换量的重载;如果两种情况完全匹配,则它更倾向于使用非模板而不是模板,使用非variardic而不是variardic。

    “最小转换量”和“完全”中有很多细节可能误导程序员。最常见的是转换 左值到 Foo const& template<class T> T&& T& 没有转换。

        2
  •  0
  •   sehe    6 年前

    operator== 的关联命名空间内的实现 Foo

    #include <boost/optional.hpp>
    
    namespace nsp {
        struct Foo { };
        bool operator==(const Foo&, const Foo&) { return false; }
    }
    
    int main() {
        boost::optional<nsp::Foo> a;
        boost::optional<nsp::Foo> b;
        return (a == b)? 0 : 1;
    }