保护 std::array 中的各个值,同时允许完全覆盖

问题描述 投票:0回答:1

我有一个全局状态的数组。这是在嵌入式/微控制器环境中运行,而不是在大型应用程序中我可能更关心全局状态。

如何声明数组,使其成员无法更改,但我仍然可以更新副本,并且在需要时仍然完全覆盖全局数组?

我有兴趣使用不可变数组,以便能够在从突变函数返回时快速检查它是否已更改。也就是说,如果内存地址发生了变化,那么我就知道它被复制和更新了。

如果已经更新了,那么我想用更新的版本覆盖原来的全局数组。

但是,我想保护数组的各个值不被 main() 中意外覆盖,或者防止全局数组被访问并直接更新。也许我有点偏执,但为了这个问题,我们可以说这是一个合理的担忧。

唯一可能的更新应通过副本/地址更新来完成。

这个版本可以用,但是全局数组可以访问并直接更新:

#include <array>
#include <iostream>

std::array<int, 3> arr = {1, 2, 3}; // global array

std::array<int, 3> const * mutate(std::array<int, 3> const * ptr) {
  // (*ptr)[2] = 9; // I like how this is prevented by const
  // return ptr; // uncomment to test no mutation

  // mutation via copy, retain new array on the heap
  std::array<int, 3> * ret = new std::array<int, 3>;
  std::copy((*ptr).begin(), (*ptr).end(), (*ret).begin());
  (*ret)[0] = 9;
  return ret;
}

int main() {
  std::array<int, 3> const * retPtr = mutate(&arr);

  if (retPtr == &arr) {
    std::cout << "pointers are the same\n";
  } else {
    std::cout << "pointers are different\n";
    arr = (*retPtr);
    // do expensive things here with the new state
  }
  delete[] retPtr;

  for (int val : arr) {
    std::cout << val;
    std::cout << "\n";
  }

  return 0;
}

首先我尝试将数组声明为

std::array<int, 3> const
。这使我无法完全覆盖数组。

std::array<int, 3> const arr = {1, 2, 3};

...

arr = (*retPtr); // Compilation error: no viable overloaded '='

然后我尝试将其声明为

std::array<int const, 3>
,但这使我无法在突变函数中对副本进行突变。

std::array<int const, 3> arr = {1, 2, 3};

std::array<int const, 3> * mutate(std::array<int const, 3> * ptr) {
  std::array<int const, 3> * ret = new std::array<int const, 3> {0,0,0};
  std::copy((*ptr).begin(), (*ptr).end(), (*ret).begin());

  // Compilation error: cannot assign to return value because function 'operator[]' 
  // returns a const value
  (*ret)[0] = 9; 

  return ret;
}

预先感谢您提供的任何帮助!

c++ immutability stdarray
1个回答
0
投票

假设您使用的是 c++20 或更高版本,现在这是可行的。 基本生活的改变使得替换对象成为可能,只要被替换的对象可以透明地替换。这限制了您不能更改数组的长度或其元素类型,因为它们会更改对象类型。

#include <iostream>
#include <array>
#include <memory>

std::array< const int, 3> a{ 1,2,3 };
void mutate()
{
    std::destroy_at(&a);  // call destructor. not needed unless array elements have destructors
    std::construct_at(&a, std::array<const int, 3> {2, 3, 4});
}

int main()
{
    mutate();
    std::cout << a[0] << '\n';
}
© www.soinside.com 2019 - 2024. All rights reserved.