【问题标题】:Handling constness of pointed values in map keys处理映射键中指向值的常量
【发布时间】:2015-03-15 21:33:33
【问题描述】:

我有以下代码:

#include <map>
using namespace std;
struct A {};

map</*const*/ A *, int> data;

int get_attached_value(const A *p) {
    return data.at(p);
}
void reset_all() {
    for (const auto &p : data) *p.first = A();
}

我的问题是,当我注释和取消注释 data 类型的 const 时,此代码因类型错误而失败。有什么办法可以在不使用const_cast 并且不丢失get_attached_value 中的const 的情况下解决这个问题?

【问题讨论】:

  • 您不能(重要!)更改 std::map/std::set 键。绝不。它会破坏 std::map/std::set 的内部数据结构,并可能导致巨大的内存泄漏/段错误/内存损坏/等。永远不要那样做。从来没有。
  • 是的,你可以让get_attached_value 接受A *
  • @nabijaczleweli 请注意,OP 没有 更改密钥。关键是指针。他们只是改变了该指针指向的对象。
  • @Angew 我看到了您的(有效)观点,但取决于实现和/或其他模板参数,这可能会中断。
  • 恕我直言,您应该使用get_attached_value 中的const_cast 解决方案。 作为该函数的实现者知道指针不会被修改,因此在那里使用const_cast 是安全的。

标签: c++ types casting stl constants


【解决方案1】:

问题似乎出在 pointee 类型 中,它在两个指针声明(映射键类型和 get_attached_value 的参数)中必须相同。

OP 的代码使用const A*,它是一个指向类A 的const 实例 的指针(另一种拼写是A const *)。在 map 声明和 get_attached_value' 参数中保留此 const 几乎可以工作,但 reset_all 不允许您为 *p.first 分配新值,因为结果类型是 A const&amp;(不能分配到) .

删除两个 const 也可以,但 OP 希望在 get_attached_value 中保留一个 const。

满足 OP 要求的一种解决方案,即尽可能多地保留 const,似乎是将指针类型更改为 const 指向 A 的非 const 实例的指针。这将使reset_all 继续工作,同时允许在映射声明和get_attached_value 的参数中使用常量指针:

#include <map>
using namespace std;
struct A {};

map<A * const, int> data;

int get_attached_value(A * const p) {
    return data.at(p);
}
void reset_all() {
    for (const auto &p : data)
        *p.first = A();
}

另一种可能的解决方案,将 map 的键作为非常量但 get_attached_value 的参数为 const,可以使用带有自定义比较器的 std::lower_bound 来替换 data.at() 调用:

#include <map>
#include <algorithm>

using namespace std;
struct A {};

map<A*, int> data;

int get_attached_value(A const * const p) {
    auto it = std::lower_bound(data.begin(), data.end(), p,
        [] (const std::pair<A* const, int>& a, A const* const b) {
            return a.first < b;
        }
    );
    return it->second;
}
void reset_all() {
    for (const auto &p : data)
        *p.first = A();
}

但是,与使用 map 的本机搜索功能 - std::lower_bound uses linear search when input iterators are not random access 的解决方案相比,此解决方案的效率要低得多。

总而言之,在 C++11 或更低版本中最有效的解决方案可能会使用 const 指针作为映射的键,并在 reset_all 函数中使用 const_cast

更多关于 const 表示法和指针的信息可以在 here 找到。

【讨论】:

  • @tohava 但是reset_all 方法无法工作,因为您正在尝试为指针分配一个新值(在这种情况下为 const 引用)。
  • 在理想的世界中,映射将是一个带有非 const 指针键的映射,并且会有一个 at 方法也适用于 const 指针(因为它只提供对值的访问,并避免将非常量指针暴露给调用者)
  • @tohava 用std::lower_bound 代替at 怎么样?那会是满足您要求的解决方案吗?但是,我认为您的问题有点不清楚并且与您之前的评论不一致,因为取消注释代码中的 const 总是会破坏 reset_all 方法。
  • 不会lower_bound要求我使用const_cast吗?
  • @tohava - 如果您的意思是 p 参数,那么是的,上面的代码适用于 p 周围的所有 const 组合。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-07-18
  • 1970-01-01
  • 2015-09-21
  • 2010-10-05
  • 1970-01-01
  • 1970-01-01
  • 2022-01-09
相关资源
最近更新 更多