不能将重载的比较运算符与Catch测试一起使用 [英] Can't use overloaded comparison operator with Catch test

查看:96
本文介绍了不能将重载的比较运算符与Catch测试一起使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用Catch 2.11.1进行了简单的单元测试:

I have a simple unit-test using Catch 2.11.1:

#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#include <utility>
#include <any>

namespace A::B
{
    namespace C
    {
        struct S
        {
        };
    }

    using type = std::pair<C::S, std::any>;
}

inline bool operator==(A::B::type const&, A::B::type const&)
{
    return true;
}

TEST_CASE("test", "[test]")
{
    auto t1 = std::make_pair(A::B::C::S(), std::any());
    auto t2 = std::make_pair(A::B::C::S(), std::any());

    REQUIRE(t1 == t2);
}

上面的简单程序会产生以下错误:

The above simple programs generates the following errors:

$ g++ -Wall -Wextra -Wpedantic test-single.cpp -std=c++17
In file included from /usr/include/c++/9/bits/stl_algobase.h:64,
                 from /usr/include/c++/9/bits/char_traits.h:39,
                 from /usr/include/c++/9/string:40,
                 from catch.hpp:457,
                 from test-single.cpp:2:
/usr/include/c++/9/bits/stl_pair.h: In instantiation of ‘constexpr bool std::operator==(const std::pair<_T1, _T2>&, const std::pair<_T1, _T2>&) [with _T1 = A::B::C::S; _T2 = std::any]’:
catch.hpp:2289:98:   required from ‘bool Catch::compareEqual(const LhsT&, const RhsT&) [with LhsT = std::pair<A::B::C::S, std::any>; RhsT = std::pair<A::B::C::S, std::any>]’
catch.hpp:2318:34:   required from ‘const Catch::BinaryExpr<LhsT, const RhsT&> Catch::ExprLhs<LhsT>::operator==(const RhsT&) [with RhsT = std::pair<A::B::C::S, std::any>; LhsT = const std::pair<A::B::C::S, std::any>&]’
test-single.cpp:28:5:   required from here
/usr/include/c++/9/bits/stl_pair.h:449:24: error: no match for ‘operator==’ (operand types are ‘const A::B::C::S’ and ‘const A::B::C::S’)
  449 |     { return __x.first == __y.first && __x.second == __y.second; }
      |              ~~~~~~~~~~^~~~~~~~~~~~

[此后还有很多其他消息...]

错误消息的关键部分是以下行:

The crucial part of the error message is this line:

/usr/include/c++/9/bits/stl_pair.h: In instantiation of ‘constexpr bool std::operator==(const std::pair<_T1, _T2>&, const std::pair<_T1, _T2>&) [with _T1 = A::B::C::S; _T2 = std::any]’:

从错误消息中很明显,正在调用的是std::pair的标准std::operator==函数,而不是重载的operator==函数.

From the error message it's clear that it's the standard std::operator== function for std::pair that is being invoked, instead of my overloaded operator== function.

如果我在Catch REQUIRE宏中进行比较,那么它会起作用:

If I don't do the comparison inside the Catch REQUIRE macro, then it works:

auto result = t1 == t2;  // Invokes my overloaded comparison operator
REQUIRE(result);

现在是Catch或我的运算符功能有问题吗?

Now is this a problem with Catch, or with my operator function?

NB:我正在Debian SID上构建最新版本的GCC 9.2

NB: I'm building on Debian SID with a recent build of GCC 9.2

$ g++ --version
g++ (Debian 9.2.1-23) 9.2.1 20200110
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

推荐答案

请注意,即使使用Lightness建议的括号,您显示的代码也非常脆弱.

Note that even with the parentheses suggested by Lightness, the code you show is exceptionally fragile.

我猜您最初是在ADL范围内,因为在宏内进行了相关名称查找(请参见 https://en.cppreference.com/w/cpp/language/adl ),并且您的代码显然不适合ADL.添加括号会使整个事情变成不合格的查找,而不是仅基于ADL的查找(再次猜测).在这种情况下,不合格查找的非ADL部分可以为您省钱,但它将与完全不相关的代码更改区分开来.

I guess you are originally in ADL-only territory due to dependent name lookup inside the macro (see the last notes of https://en.cppreference.com/w/cpp/language/adl), and your code is quite clearly not ADL-viable. Adding parentheses makes the whole thing just an unqualified lookup, not ADL-only (again, a guess). The non-ADL part of unqualified lookup saves you in this case, but it will fall apart from entirely unrelated code changes.

考虑以下代码而不是TEST_CASE,这是使用括号大概可以归结为:

Consider this code instead of the TEST_CASE, which is what using parentheses presumably boils down to:

namespace test
{
    bool foo()
    {
        auto t1 = std::make_pair(A::B::C::S(), std::any());
        auto t2 = std::make_pair(A::B::C::S(), std::any());

        return t1 == t2;
    }
}

这将按预期进行编译和运行: https://godbolt.org/z/HiuWWy

This compiles and works as expected: https://godbolt.org/z/HiuWWy

现在在全局operator==t1 == t2之间添加一个完全不相关的operator==:

Now add a completely unrelated operator== between your global operator== and the t1 == t2:

namespace test
{
    struct X{};
    bool operator==(X, X);

    bool foo()
    {
        auto t1 = std::make_pair(A::B::C::S(), std::any());
        auto t2 = std::make_pair(A::B::C::S(), std::any());

        return t1 == t2;
    }
}

然后您就可以了: https://godbolt.org/z/BUQC9Y

找不到全局名称空间中的operator==,因为(不属于ADL的部分)不合格的名称查找在 first 包含 any 的封闭范围内停止operator==.既然找不到有用的东西,它会退回到使用内置的std::pair比较运算符(通过ADL找到)的方法,该运算符将无效.

The operator== in the global namespace isn't found because the (non-ADL part of) unqualified name lookup stops in the first enclosing scope that has any operator==. Since that doesn't find anything useful, it falls back to using the inbuilt std::pair comparison operator (found via ADL), which won't work.

只需将运算符重载放在其操作的对象的名称空间中即可.因此,请不要重载std(或您不允许接触的其他名称空间)中设施的运算符.

Just put operator overloads in the namespaces of the objects they operate on. And by corollary, don't overload operators for facilities from std (or other namespaces you are not allowed to touch).

添加评论:

该标准目前还表示要考虑模板参数的名称空间,因此将operator==放在namespace C中将起作用(因为std :: pair的第一个模板参数来自此处):

The standard currently also says that the namespaces of template arguments are considered, so putting the operator== in namespace C would work (because the first template argument of std::pair comes from there): https://godbolt.org/z/eV8Joj

但是,1.与您的类型别名不能很好地融合在一起; 2.有一些使ADL减少狂野的动作,并且我看到了摆脱考虑模板参数名称空间"的讨论.参见 http://www.open-std .org/jtc1/sc22/wg21/docs/papers/2018/p0934r0.pdf :

However, 1. that doesn't mesh too well with your type alias and 2. there is some movement to make ADL less wild and I've seen discussion to get rid of the "consider namespaces of template parameters". See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0934r0.pdf:

为什么我们要研究模板参数的名称空间?除非模板参数也是基类之类,否则该类型的接口中可能没有任何内容. -香草萨特

Why on earth would we look into the namespaces of template arguments? Nothing in there could possibly be part of the interface of the type, unless the template arguments were also base classes or something. -- Herb Sutter

我不知道本文的现状,但我会避免在新代码中依赖这种ADL.

I don't know where this paper stands today but I would avoid relying on this kind of ADL in new code.

这篇关于不能将重载的比较运算符与Catch测试一起使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆