避免在 C++ 中创建新的不可变实例的样板

Avoiding boilerplate creating new immutable instances in C++

我正在使用持久数据结构。
我将有一个复杂的 class,我需要创建一个新实例但具有一个或多个具有不同值的字段。

struct Data {
  int field1; int field2; int field3; // etc.
public:
  Data withField2(int newField2) {
      return { field1, newField2, field3 };
  }
};

int main()
{
  Data d = { 1, 2, 3 };
  std::cout << d.field2 << std::endl;
  Data newD = d.withField2(4);
  std::cout << newD.field2 << std::endl;
}

最坏的情况,我可以像上面那样创建一堆 withField1(newField1Value) 方法,但是会有足够多的字段,以至于它会变得非常混乱。
另外,我可能需要多个字段的新值,所以可能会有更多。

那么,有什么神奇的说法吗

Data newData = data.with(field1Name = newField1, field2Name = newField2)

或类似的东西?

你不能那样做,但你可以用指向数据成员的指针做一些有趣的事情:

#include<iostream>
#include<functional>

struct Data {
  int field1; int field2; int field3; // etc.

  template<int Data::*M>
  Data withField(int value) {
    Data cpy = *this;
    cpy.*M = value;
    return cpy;
  }
};

int main()
{
  Data d = { 1, 2, 3 };
  std::cout << d.field2 << std::endl;
  Data newD = d.withField<&Data::field2>(4);
  std::cout << newD.field2 << std::endl;
}

这样你至少不必编写所有 withField 成员方法。

借助一些机械和元编程,也许您还可以定义一个方法来一次性更改一堆数据字段。

当然,只要您的字段都是 int.
类型就可以正常工作 否则,您需要不止一种方法或类似这样的方法:

template<typename T, T Data::*M>
Data withField(T value) {
  Data cpy = *this;
  cpy.*M = value;
  return cpy;
}

必须调用为:

Data newD = d.withField<int, &Data::field2>(4);

问题似乎是您正在重新发明 std::tuple。但是你没有重新发明 std::get<N>.

您的实现使用名称,而对于编译器而言它们是完全任意的。不能保证字段名称按数字顺序出现。