C++ Can';t在Catch测试中使用重载比较运算符
我使用Catch 2.11.1进行了一个简单的单元测试:C++ Can';t在Catch测试中使用重载比较运算符,c++,unit-testing,operator-overloading,catch2,C++,Unit Testing,Operator Overloading,Catch2,我使用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::an
#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);
}
这是Catch的问题,还是我的操作符函数的问题
注意:我正在Debian SID上构建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.
扩展操作数以提供良好诊断输出的魔力有时会失效 解决方法是使用一些括号禁用该选项:
REQUIRE((t1 == t2));
这实际上是与变量相同的解决方法
文件。在您的案例中,触发这种情况的确切原因我不确定,但从堆栈跟踪中注意到,您的
操作符==
实际上没有被调用,而是Catch::BinaryExpr::operator==
和Catch::compareEqual
,它似乎没有访问权限(或者选择不使用)您的实现。无论哪种方法,解决方案都是如上所述禁用分解机制。请注意,即使使用Lightness建议的括号,您显示的代码也异常脆弱
我猜您最初是在ADL领域,因为宏中的依赖名称查找(请参阅的最后注释),并且您的代码显然不适合ADL。添加括号会使整个过程只是一个不合格的查找,而不仅仅是ADL(再次,一个猜测)。在这种情况下,非限定查找的非ADL部分为您节省了时间,但它将与完全无关的代码更改分离
考虑这段代码而不是测试用例
,使用括号大概可以归结为:
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;
}
}
这将按照预期进行编译和工作:
现在在全局操作符==
和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;
}
}
而你就要出局了:
找不到全局命名空间中的运算符==
,因为(非ADL部分)非限定名称查找在包含任何运算符==
的第一个封闭范围内停止。由于这没有发现任何有用的东西,它转而使用内置的std::pair
比较运算符(通过ADL找到),这将不起作用
只需将运算符重载放在它们操作的对象的名称空间中。因此,不要让std
(或其他不允许接触的名称空间)中的设施的运算符过载
从评论中添加: 该标准目前还表示,模板参数的名称空间是被考虑的,因此将
操作符==
放在名称空间C
中是可行的(因为std::pair的第一个模板参数来自于此):
然而,1。这与您的类型别名和2不太匹配。有一些运动使ADL变得不那么疯狂,我看到了关于去掉“考虑模板参数的名称空间”的讨论。见:
我们究竟为什么要研究模板参数的名称空间?其中的任何内容都不可能是该类型接口的一部分,除非模板参数也是基类或其他内容。-萨特药草
我不知道这篇文章今天的立场,但我会避免在新代码中依赖这种ADL。什么是
要求的
不同之处?我想你知道,但是,只要有什么东西依赖ADL来找到你的操作符,那么所显示的代码就会失败。@MaxLanghof事实上,现在你提到了它,我相信这正是发生的事情here@LightnessRacesBY-SA3.0那么(根据您的回答)为什么要使用括号呢?或者不是吗?@MaxLanghof和帕伦斯一起,它只是一个bog标准,所以ADL不被依赖,对吗?因为该函数在作用域中。这是我第一次尝试,将运算符放在A::B
命名空间中。它仍然不起作用,但更糟糕的是,Lightness Races by-SA3.0(使用括号)的解决方案不再起作用。因此,是的,我的设计中确实存在很多脆弱性,我可能应该重新考虑一下。@Someprogrammerdude的操作符==
在std::pair
上运行,因此必须在命名空间std
中才能找到它。该标准目前还表示,模板参数的名称空间是被考虑的,因此将运算符==
放在名称空间C
中是可行的(因为std::pair
的第一个模板参数来自于此),但这是1。与您的类型
别名和2不太匹配。有一些运动使ADL变得不那么疯狂,“考虑模板参数的名称空间”是afaik的砧板。看,找不到后者的源…根据您的回答,我将类型
转换为一个新结构,并将比较运算符移动到a::B
命名空间中。它现在运行良好。
REQUIRE((t1 == t2));
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;
}
}
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;
}
}