结构体的通用比较运算符

11

在我的许多单元测试中,我需要比较仅具有数据成员的简单结构体的内容:

struct Object {
  int start;
  int stop;
  std::string message;
}
现在,如果我想写这样的内容:
CHECK(object1==object2);

我总是需要实现:

bool operator==(const Object& lhs, const Object& rhs) {
   return lhs.start==rhs.start && lhs.stop==rhs.stop && lhs.message=rhs.message;
}

编写所有这些比较函数非常繁琐,而且容易出错。 想象一下,如果我向Object添加一个新的数据成员,但是比较运算符没有更新,会发生什么。

然后我想起了我在Haskell中的知识和神奇的deriving(Eq)指令,它可以自动生成一个健全的比较函数。

那么,我如何在C++中派生出类似的东西呢?

幸运的是,我发现C++17带有一个通用的operator==,每个结构体都可以通过std::make_tuple函数方便地转换为一个std::tuple元组。

因此,我大胆尝试了以下内容:

#include <tuple>
#include <iostream>
#include <tuple>

template<typename T>
bool operator==(const T& lhs, const T& rhs)
{
    auto leftTuple = std::make_tuple(lhs);
    auto rightTuple = std::make_tuple(rhs);

    return leftTuple==rightTuple;
}

struct Object
{
    std::string s;
    int i;
    double d;
};

int main(int arg, char** args)
{
    std::cout << (Object{ "H",1,2. } == Object{ "H",1,2. }) << std::endl;
    std::cout << (Object{ "A",2,3. } ==  Object{ "H",1,2. }) << std::endl;
    return EXIT_SUCCESS;
}

但是,不幸的是,它只是无法编译,我真的不知道为什么。Clang告诉我:

main.cpp:11:18: error: use of overloaded operator '==' is ambiguous (with operand types
      'std::tuple<Object>' and 'std::tuple<Object>')
        return leftTuple==rightTuple;

我是否能够修复这个编译错误,以获得我想要的行为?


1
你真的需要这个结构体吗?你能否只使用元组呢? - n. m.
1个回答

8
不可以,因为比较元组会回退到比较元组的元素,所以leftTuple == rightTuple试图比较两个无法比较的Object
每个结构体都应该可以通过std::make_tuple轻松转换为std::tuple,但实际上你只会得到一个包含这个结构体的单一元素tuple
关键在于使用std::tie
std::tie(lhs.mem1, lhs.mem2) == std::tie(rhs.mem1, rhs.mem2)

但这与您原始的解决方案存在相同的问题。不幸的是,C++17没有任何避免此问题的功能您可以编写宏 :)。但在C++20中,您将能够执行以下操作:

struct Object
{
    std::string s;
    int i;
    double d;
    bool operator==(const Object &) const = default;
};

这将为 Object 生成正确的比较运算符。


谢谢。我对std::make_tuple的作用有误解。 - Aleph0
似乎我正在寻找std::make_from_tuple的相反操作以使其正常工作。 - Aleph0
@Aleph0 是的,你需要像create_tuple_from这样的东西,但不幸的是没有反射是不可能实现的。 - Rakete1111

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