C++ 在常量向量中存储常量*

C++ 在常量向量中存储常量*,c++,c++11,vector,c++14,constants,C++,C++11,Vector,C++14,Constants,我必须处理的相关接口代码由两个函数组成,一个用于检索对象,另一个用于将对象作为向量提交。问题是,检索函数返回const Object*,而提交函数需要const vector 我知道这是可以用const_cast解决的,但是有没有其他更干净的方法 下面是演示问题的代码: #包括 ////////////代表性界面实现,请勿触摸/////// 结构对象{}; 类接口{ 公众: size_t getNumObjects()常量{return 10;} 常量对象*getObject(大小索引)常量{r

我必须处理的相关接口代码由两个函数组成,一个用于检索对象,另一个用于将对象作为向量提交。问题是,检索函数返回
const Object*
,而提交函数需要
const vector

我知道这是可以用
const_cast
解决的,但是有没有其他更干净的方法

下面是演示问题的代码:

#包括
////////////代表性界面实现,请勿触摸///////
结构对象{};
类接口{
公众:
size_t getNumObjects()常量{return 10;}
常量对象*getObject(大小索引)常量{return nullptr;}
};
常量接口;
无效提交对象(const std::vector&objects);
//////////////////////////////////////////////////////////////////////
//任务:从“接口”中获取所有对象并将其提交到“submitObjects”。
int main(){
向量对象;
对于(size_t i=0;i
我能想出的解决这个问题的唯一方法是将
对象
a
std::vector
,然后用
对象插入对象,但我觉得必须有更好的解决方案

欢迎提出任何意见


有关这是一个有效问题的更多背景信息:
const-vector
只能传递
const-Object*
,如其所有getter重载所示,如下所示:

因此,
vector
几乎从未在接口中使用。因此,将大量的
常量对象*
组合在一个大
常量向量
中的想法是一个有效的概念,但如果没有
常量投射
,似乎不可能构建

编辑:上述陈述不正确,感谢澄清。
const std::vector
可以生成非常量指针,因为我对指针常量的理解不正确。

您的接口返回指向常量对象的指针。您希望将这些常量对象发送到需要可变对象的函数中

这不仅仅是关于未定义的行为,而是关于惊喜

想象一下,实现接口的一个正在保留有关对象的计算值,并且在调用可能发生变化的方法时标记为脏。确实返回指向常量对象的指针是有充分理由的。修改const对象将破坏类不变量

如果您将该常量发送到
submitObject
,并且您的对象在那里发生了变异,那么您在某种意义上撒谎了:接口向您发送常量对象,而您对其进行了变异。你违反了合同

如果
submitObjects
不改变其参数,那么就没有理由保持参数可变


为了消除对常量的混淆,下面是指向可变数据的指针的常量向量的示例:

// const std::vector<int*>
auto const cannot_erase_or_add = std::vector<int*>{
    new int{1},
    new int{2},
    new int{3}
};

// Error!
// cannot_erase_or_add.push_back(new int{4});

// Error!
// cannot_erase_or_add.clear();

// operator[] return int*, because it's a vector of int*
int* one = cannot_erase_or_add[0];

// totally legal, int*, no const there
*one = 2;
//常数std::vector
自动常量无法\u擦除\u或\u添加=标准::向量{
新int{1},
新int{2},
新整数{3}
};
//错误!
//无法擦除或添加。推回(新int{4});
//错误!
//无法擦除或添加.clear();
//运算符[]返回int*,因为它是int的向量*
int*one=无法擦除或添加[0];
//完全合法,整数*,没有常数
*1=2;
另一方面,情况正好相反:

// std::vector<int const*>
auto can_erase_or_add = std::vector<int const*>{
    new int{1},
    new int{2},
    new int{3}
};

// Works
can_erase_or_add.clear();

// Works
can_erase_or_add.push_back(new int{4});

// operator[] return int const*, because it's a vector of int const*
int const* four = can_erase_or_add[0];

// Can't do that, pointer to const
// *four = 2;
//std::vector
自动可擦除或添加=标准::向量{
新int{1},
新int{2},
新整数{3}
};
//工作
可以擦除或添加.clear();
//工作
可以擦除或添加。推回(新的int{4});
//运算符[]返回int const*,因为它是int const的向量*
int const*four=可以擦除或添加[0];
//不能那样做,指针指向常量
//*4=2;
您可以选择:

  • submitObject
    更改为接受
    std::vector
  • 或者更改
    接口::getObject
    以返回指向非常量
    对象的指针
  • 或者使用
    const\u cast
    ,如您所想。尽可能避免这种情况
  • 如果
    submitObjects
    修改指向的对象,则3。如果不应修改指向的对象(例如,如果它们实际上是常量对象),则会造成危险。如果可以修改对象,则选择2

    如果
    submitObjects
    未修改指向的对象,则执行更改1应该没有问题

    如果
    submitObjects
    修改了指向的对象,并且要修改的对象不合适,那么就存在逻辑矛盾,除了不将指针传递给函数之外,没有其他解决方案

    无法从常量向量中检索可变指针

    只有指针是不能变异的。但是,您可以对指向的对象进行变异。例如:

    struct Object{ int member; };
    Object o;
    const vector<Object*> objects{&o};
    objects[0]->member = 42; // well-defined
    
    struct对象{int member;};
    对象o;
    常量向量对象{&o};
    对象[0]->成员=42;//明确的
    
    我考虑的是1,但这意味着它将不再接受std::vector,需要另一个重载


    或者,您可以使用模板。特别是,这可能是输入迭代器或范围的一个很好的用例。

    您误解了文档/键入规则。如果有
    T=Object*
    ,则
    const T
    不是
    const Object*
    ,而是
    Object*const
    。这就是C和C++如何工作的。< /P> 因此,您的断言“您无法从
    常量向量
    获取可变对象”是错误的。您不能做的是修改向量或元素的值(即更改指针)。但是访问器返回对
    对象*常量
    的引用,因此仍然可以修改指向的对象:

    void foo(const std::vector<int*>& v)
    {
        (*v[0])++; // Increments the integer pointed to by the first vector element.
    }
    


    胃肠道
    struct X
    {
        int* y;
    };
    
    void foo(const X& x)
    {
        (*x.y)++;
    }