C++ 使用混合常量和非常量元素模拟std::vector

C++ 使用混合常量和非常量元素模拟std::vector,c++,constants,C++,Constants,我想模拟一个包含常量和非常量元素的std::vector。更具体地说,我想让函数在一个向量上运行,并且可以看到整个向量,但只能写入特定的元素。可以写入和不能写入的元素将在运行时确定,并可能在运行时更改 一种解决方案是创建一个容器,其中包含一个元素数组和一个大小相等的布尔数组。所有非常量访问都将通过一个函数进行,该函数检查布尔数组的写入是否有效,否则抛出异常。这样做的缺点是在每次写入时都添加一个条件 第二种解决方案可能是使用相同的容器,但这次写访问是通过将数组编辑函数传递给容器的成员函数来完成的。

我想模拟一个包含常量和非常量元素的std::vector。更具体地说,我想让函数在一个向量上运行,并且可以看到整个向量,但只能写入特定的元素。可以写入和不能写入的元素将在运行时确定,并可能在运行时更改

一种解决方案是创建一个容器,其中包含一个元素数组和一个大小相等的布尔数组。所有非常量访问都将通过一个函数进行,该函数检查布尔数组的写入是否有效,否则抛出异常。这样做的缺点是在每次写入时都添加一个条件

第二种解决方案可能是使用相同的容器,但这次写访问是通过将数组编辑函数传递给容器的成员函数来完成的。容器成员函数将允许数组编辑函数对数组进行编辑,然后检查它是否没有写入不可写的元素。这样做的缺点是,数组编辑函数可能会偷偷摸摸地将非常量指针传递给数组元素,让容器函数检查是否一切正常,然后写入不可写的元素

最后一个问题似乎很难解决。提供直接可写访问似乎意味着我们必须始终假定直接可写访问

有更好的解决方案吗

编辑:Ben的评论有一个很好的观点,我应该在问题中加以说明:为什么不是常量向量和非常量向量


问题是,我想到的场景是,我们的元素在概念上是单个数组的一部分。它们在该数组中的位置是有意义的。要使用常量和非常量向量,需要将概念上存在的单个数组映射到实现它的两个向量。此外,如果可写元素列表发生变化,则需要移动两个向量中的元素或指针。

我认为您可以使用下面的类来完成所需的操作,该类非常简化,以说明主要概念

template <typename T>
struct Container
{
   void push_back(bool isconst, T const& item)
   {
      data.push_back(std::make_pair(isconst, item));
   }

   T& at(size_t index)
   {
      // Check whether the object at the index is const.
      if ( data[index].first )
      {
         throw std::runtime_error("Trying to access a const-member");
      }
      return data[index].second;
   }

   T const& at(size_t index) const
   {
      return data[index].second;
   }

   T const& at(size_t index, int dummy) // Without dummy, can't differentiate
                                        // between the two functions.
   {
      return data[index].second;
   }

   T const& at(size_t index, int dummy) const // Without dummy, can't differentiate
                                              // between the two functions.
   {
      return data[index].second;
   }

   std::vector<std::pair<bool, T> > data;
};
模板
结构容器
{
无效推回(bool isconst、T const和item)
{
数据。推回(std::make_pair(isconst,item));
}
T&at(规模指数)
{
//检查索引处的对象是否为常量。
if(数据[索引].first)
{
抛出std::runtime_错误(“尝试访问常量成员”);
}
返回数据[索引]。第二;
}
T常数和at(大小索引)常数
{
返回数据[索引]。第二;
}
T const&at(size\u T index,int dummy)//没有dummy,无法区分
//在这两个功能之间。
{
返回数据[索引]。第二;
}
T const&at(size\u T index,int dummy)const//没有dummy,无法区分
//在这两个功能之间。
{
返回数据[索引]。第二;
}
std::矢量数据;
};
这是一个测试程序及其输出

#include <stdio.h>
#include <iostream>
#include <utility>
#include <stdexcept>
#include <vector>

//--------------------------------
// Put the class definition here.
//--------------------------------

int main()
{
   Container<int> c;
   c.push_back(true, 10);
   c.push_back(false, 20);

   try
   {
      int value = c.at(0); // Show throw exception.
   }
   catch (...)
   {
      std::cout << "Expected to see this.\n";
   }

   int value = c.at(0, 1); // Should work.
   std::cout << "Got c[0]: " << value << "\n";

   value = c.at(1); // Should work.
   std::cout << "Got c[1]: " << value << "\n";

   value = c.at(1, 1); // Should work.
   std::cout << "Got c[1]: " << value << "\n";

   // Accessing the data through a const object.
   // All functions should work since they are returning
   // const&.
   Container<int> const& cref = c;

   value = cref.at(0); // Should work.
   std::cout << "Got c[0]: " << value << "\n";

   value = cref.at(0, 1); // Should work.
   std::cout << "Got c[0]: " << value << "\n";

   value = cref.at(1); // Should work.
   std::cout << "Got c[1]: " << value << "\n";

   value = cref.at(1, 1); // Should work.
   std::cout << "Got c[1]: " << value << "\n";

   // Changing values ... should only work for '1'
   try
   {
      c.at(0) = 100; // Show throw exception.
   }
   catch (...)
   {
      std::cout << "Expected to see this.\n";
   }

   c.at(1) = 200; // Should work.
   std::cout << "Got c[1]: " << c.at(1) << "\n";
}
#包括
#包括
#包括
#包括
#包括
//--------------------------------
//把类定义放在这里。
//--------------------------------
int main()
{
容器c;
c、 推回(真,10);
c、 推回(假,20);
尝试
{
int value=c.at(0);//显示抛出异常。
}
捕获(…)
{

std::cout在运行时使用const有什么意义?您可能有一个函数,它接受一个向量,应该只写入某些元素,但可能需要查看所有元素。const在编译时是用来保护您的。如果您试图写入标记为const的内容,它将无法编译。一旦程序运行,它就帮不了您。只要有一个常量向量和一个非常量向量。我不明白你的第二个解决方案描述的是什么,但它听起来比每次写入时检查布尔值更昂贵。我认为它还有一个缺点,就是直到常量数据被销毁后才发现运行时常量冲突。@Ben,这可能行得通,但你怎么知道从一个索引的元素列表和一个变化的应该可写的元素列表中看不出来吗?你必须在两个向量之间移动元素或指针,但如何使用合理的接口对我来说并不明显。为什么你需要带有
参数的方法?前两个
不是at
方法吗(一个常量和一个非常量)够了吗?@Mikhail如果你有一个
非常量
容器,并且你想从中获取一个
常量
项,你必须使用带有
参数的重载函数。否则,你无法获取它。在这种情况下,你为什么需要第二个带有伪的方法:
t const&at(size\u t index,int-dummy)const
?如果容器是const,则将调用
t const&at(size\u t index)const
。@Mikhail,这样您也可以将
at(index,0)
用于const容器。 Expected to see this. Got c[0]: 10 Got c[1]: 20 Got c[1]: 20 Got c[0]: 10 Got c[0]: 10 Got c[1]: 20 Got c[1]: 20 Expected to see this. Got c[1]: 200