c++ - 使用带有 boost::optional 的相等运算符

标签 c++ boost argument-dependent-lookup

我正在尝试为在另一个命名空间中定义的类型 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 )

发生了什么?我的猜测是这与 Koenig 查找规则有关......

最佳答案

立即修复
做这个:

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;
    }
  };
}
如果 Foo,这是最佳的是一个模板类。
您的解决方案出了什么问题
这里发生的事情是optionalstd (或 boost 或其他)并在该命名空间中尝试执行 nsp::Foo == nsp::Foo称呼。
有一个==这不适用于 ::std命名空间,所以它不会在 :: 中查找;一旦找到任何 ==它停止寻找,即使论点完全无关。它还寻找 ==在与参数关联的命名空间中——在本例中 ::nsp .但它从不查找 ::也在这里。
向类型添加运算符时,始终在类型的命名空间中定义运算符。
命名空间可以重新打开。因此,如果您无法控制头文件,则可以使用 == 创建一个新的头文件。在里面。此 ==必须在 optional<nsp::Foo>::operator== 的每个点都可见被调用或您的程序由于 ODR 违规而格式错误(并且,在这种情况下,还会生成编译器错误,这有助于避免黑化错误)。
长版
当您调用运算符(或函数)时,查找遵循几个简单的步骤。
首先它在本地(在本地命名空间中)查看。如果它在那里找到任何东西,这个搜索就会停止。 (这包括注入(inject)命名空间的 using ns::identifier; 名称,但通常不包括 using namespace foo; )。即使函数或运算符不适用于相关类型,也会发生这种“停止”;任何 ==对于命名空间中的任何类型,都会停止此搜索。
如果找不到匹配项,它将开始在封闭的命名空间中查找,直到找到函数/运算符,或到达根命名空间。如果有using namespace foo;声明,这些命名空间中的函数/操作符被认为在 using namespace 的“公共(public)父”命名空间中。位置和正在导入的命名空间。 (所以 using namespace std;namespace foo 中,看起来 std:: 中,而不是在 foo 中)。
结果生成一组用于重载解析的候选对象。
接下来,完成 ADL(参数相关查找)。检查所有函数/运算符参数的关联命名空间。此外,还会(递归地)检查模板的所有类型参数的关联命名空间。
收集与名称匹配的运算符/函数。对于 ADL,不检查父命名空间。
这两个运算符/函数集合是重载解析的候选对象。
在您的情况下,命名空间 where ==被称为 boost . boost有很多==运算符(即使它们不适用),所以所有 ==boost是候选人。
接下来,我们检查参数的命名空间——nsp::Foo在这种情况下。看nsp看不到 == .
然后我们对这些运行重载决议。没有候选人工作,你会得到一个编译器错误。
现在,当您移动用户定义的 == 时进入 namespace nsp ,然后将其添加到 == 的集合中在 ADL 步骤中找到。当它匹配时,它被调用。
重载决议(它对候选人的作用)是它自己的复杂主题。简而言之,它试图找到涉及最少转换量的重载;如果两个 case 彼此完全匹配,则它更喜欢非模板而不是模板和非可变参数而不是可变参数。
“最少转换”和“完全”中有很多细节可能会误导程序员。最常见的是转换一个Foo左值到 Foo const&是少量的转换,转换为template<class T> T&&T&是没有转换。

关于c++ - 使用带有 boost::optional 的相等运算符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51951195/

相关文章:

c++ - 没有 typedef 的函数指针

c++ - 我可以同时复制一个 shared_ptr 吗?

c++ - std::vector(InputIterator first, InputIterator last) 是线性时间复杂度吗?

相当于tailq的C++

c++ - 如何使用可变模板参数为元组专门化类模板?

c++ - 使用 std 或 boost 库的 C++ 中的 Qtimer 等效于什么?

c++ - 使用 boost Zlib 时未解析的外部符号 inflateEnd(和其他)

c++ - 由于 ADL,对模板函数的调用不明确

c++ - 如何为全局命名空间中的类提供 swap()?

c++ - 是否有可能让 Phoenix 对二元运算符的贪婪程度降低一个档次?