STL std::map,通过ref传递给const以及const_casting的必要性

发布于 2024-10-04 18:06:00 字数 1445 浏览 4 评论 0原文

我有一个关于 const_cast 的简单问题以及有关 STL 容器的最佳实践。考虑以下情况,其中类 Foo 具有从 Widget*int 的私有 STL std::map

声明:

#include <map>  
using std::map;

class Widget;

class Foo {
public:
     Foo(int n);
     virtual ~Foo();

     bool hasWidget(const Widget&);

private:
     map<Widget*,int> widget_map;
};

定义:

#include <map>
#include "Foo.h"
#include "Widget.h"

using std::map;

Foo::Foo(int n)
{
     for (int i = 0; i < n; i++) {
          widget_map[new Widget()] = 1;
     }
}

Foo::~Foo()
{
     map<Widget*, int>::iterator it;
     for (it = widget_map.begin(); it != widget_map.end(); it++) {
          delete it->first;
     }
}

bool Foo::hasWidget(const Widget& w)
{
     map<Widget*, int>::iterator it;
     it = this->widget_map.find(const_cast<Widget*>(&w));
     return ( ! ( it == widget_map.end() ) );
}

鉴于 hasWidget 将 const 引用作为其参数,因此在调用 map::find 时需要丢弃 constness(wiget_mapWiget*int)。据我所知,这种方法既明智又可取——但如果没有更有经验的 C++ 程序员的反馈,我不愿意接受它。

在我看来,这是适当使用 const_cast 的少数情况之一,因为我们将转换结果传递给 STL 方法。我说得对吗?

我意识到这个问题的其他排列已经被提出(例如, const_cast for vector with object),但似乎没有一个直接解决上述问题。

提前致谢。

I have a simple question regarding const_cast and best practices regarding STL containers. Consider the following where class Foo has a private STL std::map from Widget* to int:

Declaration:

#include <map>  
using std::map;

class Widget;

class Foo {
public:
     Foo(int n);
     virtual ~Foo();

     bool hasWidget(const Widget&);

private:
     map<Widget*,int> widget_map;
};

Definition:

#include <map>
#include "Foo.h"
#include "Widget.h"

using std::map;

Foo::Foo(int n)
{
     for (int i = 0; i < n; i++) {
          widget_map[new Widget()] = 1;
     }
}

Foo::~Foo()
{
     map<Widget*, int>::iterator it;
     for (it = widget_map.begin(); it != widget_map.end(); it++) {
          delete it->first;
     }
}

bool Foo::hasWidget(const Widget& w)
{
     map<Widget*, int>::iterator it;
     it = this->widget_map.find(const_cast<Widget*>(&w));
     return ( ! ( it == widget_map.end() ) );
}

Given that hasWidget takes a reference to const as its parameter, the constness needs to be cast away when calling map::find (wiget_map being from Wiget* to int). As far as I can tell, this approach is both sensible and desirable -- but I'm reluctant to accept it as such without feedback from more experienced C++ programmers.

It seems to me that this is one of the few cases of using const_cast appropriately given that we're passing the result of the cast to an STL method. Am I correct?

I realise that other permutations of this question have been posed already (for example, const_cast for vector with object) but none seem to directly address the above.

Thanks in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

箜明 2024-10-11 18:06:01

为什么不改变 hasWidget 来获取 Widget* 呢?目前该界面很危险,因为它意味着您正在按底层映射中的值查找小部件,而实际上您是按地址查找它们。我认为该方法也应该是 const:

bool hasWidget(Widget *) const;

Why not change hasWidget to take a Widget*? The interface is dodgy at the moment, because it implies that you're looking for Widgets by value in the underlying map, when you're actually looking for them by address. The method should also be const, I reckon:

bool hasWidget(Widget *) const;
老娘不死你永远是小三 2024-10-11 18:06:01

带有键(键为指针)的映射很笨重 - 查找它的唯一方法是拥有相同的指针。为此,您必须保证使用具有相同地址的对象调用 hasWidget 方法!

当然,您应该正确实现 Widget,以便它重载正确的运算符以充当 std::map 中的键!在你的地图中,你可以简单地拥有:

std::map<Widget, int>

然后你的发现不需要const_cast

A map with a key where the key is pointer is unwieldy - the only way to look it up is to have the same pointer. For this to work, you have to guarantee that the the hasWidget method will get called with an object that has the same address!

Surely you should implement Widget properly such that it has the correct operators overloaded to act as a key in a std::map! In your map, you can then simply have:

std::map<Widget, int>

And then your find doesn't need a const_cast!

奢华的一滴泪 2024-10-11 18:06:01

这对我来说看起来很笨重。通过物理地址来识别对象是相当“特殊”的,诚然它是独一无二的,但也很奇怪。

我强烈考虑反转映射:

std::map<Widget::Id, Widget*>

其中 Widget::Id 可以简单地是 int 或类似的。

那么const-ness就不会有任何问题了。

要深入研究,您还可以查看 Boost指针容器库:

boost::ptr_map<Widget::Id, Widget>

这将缓解内存管理问题。

This looks clunky to me. Identifying objects by their physical addresses is quite "special", admittedly it's unique, but it's weird too.

I would strongly consider reversing the map:

std::map<Widget::Id, Widget*>

where Widget::Id could simply be an int or similar.

There would not be any issue with the const-ness then.

To delve deeper, you could also have a look at the Boost Pointer Container library:

boost::ptr_map<Widget::Id, Widget>

which would alleviate the memory management issues.

猫九 2024-10-11 18:06:00

我想我的回答会陷入“主观和争论”,但我会尝试一下......

我并不被 const_cast 吓到,但我对你的设计持怀疑态度。成员函数 hasWidget 通过 const ref 获取其参数:这对客户端意味着什么?从客户端的角度来看,如果我不知道实现,我可能会认为每个 Widget按值与范围。对我来说,界面并不反映实际行为,它通过地址比较 Widget

例如,当前签名允许传递临时 Widget,尽管在​​这种情况下返回值永远不可能是 true。我会亲自将签名更改为(请注意,我添加了一个const):

bool hasWidget(const Widget *) const;

I think I'm going to fall in the 'subjective and argumentative' through my answer, but I'll give it a shot...

I'm not horrified by the const_cast, but I'm skeptical on your design. The member function hasWidget takes its parameter by const ref : what does this say to the client ? From a client point of view, if I didn't know the implementation, I would probably think that each Widget is compared by value with the parameter. For me, the interface does not reflect the actual behavior, which compares the Widget by address.

For example, the current signature allows a temporary Widget to be passed, although the return value could never be true in this case. I would personally change the signature to (note that I added a const) :

bool hasWidget(const Widget *) const;
北陌 2024-10-11 18:06:00

是的,这是 const_cast 的合理使用。您应该考虑将 hasWidget 设置为 const

Yes, that's a reasonable use of const_cast<>. You should consider making hasWidget const.

灯下孤影 2024-10-11 18:06:00

为什么不使用 map?您似乎从未修改过地图中任何键指向的小部件。

假设有充分的理由,那么是的,我认为你是对的。当调用保证不会修改指针的引用对象的代码时,可以安全地放弃 const。由于指针容器的模板化方式,它们的函数都不会直接修改该引用对象,但如果包含的类型是 const 指针,则用户也无法修改该引用对象(无需 const 强制转换)。如果它必须是两者之一,那么在搜索之前抛弃 const 肯定比在修改之前抛弃 const 更安全......

顺便说一句,如果您使用 count<,hasWidget 会更短/code> 而不是 find。一般来说,使用 count 也稍微比 const 更安全(在本例中除外),因为使用此 const_castfind 返回一个迭代器,该迭代器可以是用于修改 Widget,而 count 则不然。所以你不必担心count的返回值会发生什么。显然,无论如何,返回值都完全受到控制。

Why not use a map<const Widget*,int>? You don't seem to ever modify the Widget pointed to by any of the keys in your map.

Assuming there's a good reason then yes, I think you're right. When calling code which is guaranteed not to modify the referand of the pointer, it's safe to cast away const. Because of the way containers of pointers are templated, none of their functions ever directly modify that referand, but if the contained type were a const pointer, then users wouldn't be able to modify the referand either (without a const cast). It's certainly safer to cast away const before searching, than to cast away const before modifying, if it must be one of the two...

Btw, hasWidget would be shorter if you use count rather than find. It's also marginally const-safer in general (not in this case) to use count, because find with this const_cast returns an iterator that could be used to modify the Widget, whereas count doesn't. So you don't have to worry what happens to the return value of count. Obviously here that return value is entirely under control anyway.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文