对容器中项目的 const-correct 指针访问

Posted

技术标签:

【中文标题】对容器中项目的 const-correct 指针访问【英文标题】:Const-correct pointer access to items in a container 【发布时间】:2016-07-12 10:34:14 【问题描述】:

问题

我有一个类持有指向容器的指针。容器提供对其项目子集的访问。这是通过具有 const 和 non-const 版本的方法实现的,该方法返回 const/non-const 指针向量。以下代码摘自更复杂的设置的最小(不完全功能):

#include <vector>

struct Entity
 int dummy; ;

template <typename Value>
struct MyContainer

    typedef Value* pointer;
    typedef Value const* const_pointer;

    std::vector<pointer> pointers() 
        std::vector<pointer> ret;
        return ret;
    
    std::vector<const_pointer> pointers() const 
        std::vector<const_pointer> ret;
        return ret;
    
;

struct MyType

    typedef MyContainer<Entity> ContainerType;
    ContainerType* m_data;   // I cannot make this a non-pointer attribute

    std::vector<Entity*> pointers() 
        std::vector<Entity*> ret = m_data->pointers();
        return ret;
    
    std::vector<Entity const*> pointers() const 
        std::vector<Entity const*> ret = m_data->pointers();   // error!
        return ret;
    
//  std::vector<Entity*> pointers() const 
//      std::vector<Entity*> ret = m_data->pointers();
//      return ret;
//  
;

int main() 
    MyType obj;
    MyType const& constobj = obj;
    std::vector<Entity*> pointers = obj.pointers();
    std::vector<Entity const*> constpointers = constobj.pointers();
//  std::vector<Entity*> constpointers = constobj.pointers();

Clang 3.8.0(也是3.5.2)报错:

error: no viable conversion from 'vector<pointer>' to 'vector<const Entity *>'
            std::vector<Entity const*> ret = m_data->pointers();
                                       ^     ~~~~~~~~~~~~~~~~~~

commected 代码编译得很好,但是,我的意图是让 const 访问通过 const 访问路径(通过 constobj)获得的项目,因此 const 方法应该返回 Entity const* 类型的指针。将m_data 的类型从ContainerType* 更改为ContainerType 可以解决问题,但这不是我原始代码中的选项。

问题:

    似乎MyType::pointers() const 通过其属性m_data 调用了非常量方法MyContainer::pointers(),该属性的类型为MyContainer* const(至少我是这么认为的)。我显然需要输入MyContainer const*。有没有优雅的解决方案来实现这一目标? 不太优雅的解决方案是将指针强制转换为 const 指针:std::vector&lt;Entity const*&gt; ret = static_cast&lt;ContainerType const*&gt;(m_data)-&gt;pointers(); 这被认为是明智的解决方案吗? 更笼统地说:我的设计有缺陷,还是我只是遗漏了一个细节?

免责声明: 我希望这不是重复的,看来其他人以前一定遇到过同样的问题。无论如何,我没有通过阅读有关 SO 的相关问题来解决这个问题。 (问题 Const correctness causing problems with containers for pointers? 不同,它是一个指针容器。这个问题似乎是相关的,但答案并没有帮助我解决我目前的问题。)

【问题讨论】:

m_data 在您的调用中不是const,因此将再次调用非常量重载。 STL 采用了明确命名的常量方法和返回类型的设计,即std::vector::cbegin()。它适合您吗? @Sergey 好点。值得作为答案发布。 @Sergey Hm.... 我不明白这如何解决我的问题。你的意思是我应该返回一个迭代器向量而不是一个指针向量? 如果您可以使用实验性功能,您可能会对propagate_const感兴趣 【参考方案1】:

当你写作时

m_data->pointers()

您调用的是同一个函数(非常量函数),因为 m_data 定义为:

ContainerType* m_data;

即指向非 const 对象的指针。

当你使用 constobj 时,编译器认为 m_data 是一个 const 成员(你不能改变 constobj),但是在这个对象中,指针仍然指向一个非 const 对象。

我不知道您的应用程序是什么,但您可以考虑添加一个新函数作为“cpointers”以返回 const 指针,即使对象不是 const。

编辑:我的意思是,像这样:

struct MyContainer

    typedef Value* pointer;
    typedef Value const* const_pointer;

    std::vector<pointer> pointers() 
        std::vector<pointer> ret;
        return ret;
    
    std::vector<const_pointer> pointers() const 
        std::vector<const_pointer> ret;
        return ret;
    
    std::vector<const_pointer> cpointers() const 
        std::vector<const_pointer> ret;
        return ret;
    
;

struct MyType


    typedef MyContainer<Entity> ContainerType;
    ContainerType* m_data;   // I cannot make this a non-pointer attribute

    std::vector<Entity*> pointers() 
        std::vector<Entity*> ret = m_data->pointers();
        return ret;
    
    std::vector<Entity const*> pointers() const 
        std::vector<Entity const*> ret = m_data->cpointers();   // error!
        return ret;
    
;

【讨论】:

不相关;问题是 OP 正在尝试执行不存在的隐式转换。【参考方案2】:

改变

std::vector<Entity const*> ret = m_data->pointers();   // error!

std::vector<Entity const*> ret = 
(static_cast<const MyContainer<Entity>*>(m_data))->pointers(); // OK

【讨论】:

这正是我在第二个问题中提出的解决方案。您能否回答这个问题,即您认为这是“正确”的解决方案吗?为什么?

以上是关于对容器中项目的 const-correct 指针访问的主要内容,如果未能解决你的问题,请参考以下文章

智能指针的 const 正确性

Swig - 绑定一个返回对导向器类指针的引用的方法

C语言中数组与指针的异同之处!你不知道的编程奥秘~

C++ const 正确性漏洞或意外使用?

C++基类指针容器,编辑特定对象的属性

是否可以安全地使用指向向量元素的指针来确定其在容器中的位置?