【问题标题】:Is it possible to cast a pair<Key, Value> to a pair<const Key, Value>?是否可以将 pair<Key, Value> 转换为 pair<const Key, Value>?
【发布时间】:2012-01-08 11:59:12
【问题描述】:

所以我有一个智能迭代器来模拟映射 const_iterator,它需要在内部构建返回类型。显然,我想在我的迭代器类中存储一个pair&lt;Key, Value&gt;(因为我需要修改它),但同时我希望解引用函数呈现一个pair&lt;const Key, Value&gt;(实际上它是一个@分别为 987654324@ 和 const pair&lt;const Key, Value&gt;*)。到目前为止,我提出的唯一解决方案是每次更改我的迭代器类指向的值更改时动态分配一个新对。不用说,这不是一个好的解决方案。

我也尝试过*const_cast&lt;const pair&lt;const Key, Value&gt; &gt;(&amp;value),其中value 被声明为pair&lt;Key, Value&gt;

任何帮助将不胜感激(知道无法完成)。

编辑

出于好奇:我最终在我的迭代器类中存储了一个pair&lt;const Key, Value&gt; p。为了更改对,我根据底层迭代器 (map&lt;Key, Value&gt;::const_iterator it) 分别更改两个元素,const_casting 键以便可以更改它,如下所示:

*const_cast<Key*>(&p.first) = it->first;
p.second = it->second;

不是我非常满意的解决方案,但它可以完成工作,并且取消引用方法很高兴,因为我存储了正确类型的东西,它们可以引用。

【问题讨论】:

  • 很难相信你会不遗余力地发布这篇小论文,甚至没有尝试std::pair&lt;int, double&gt; p(1,2); std::pair&lt;const int, double&gt; q = p; // fine
  • @Kerrek SB:我认为你忽略了 Op 问题的一个重要部分。他希望避免在每次调用他的自定义迭代器的取消引用/成员选择运算符时构造一个新的对对象。这在 operator-> 中变得尤为重要,因为这意味着他的迭代器必须潜在地存储一个成员对并在每次调用函数或迭代器递增/递减时复制到它。
  • @KerrekSB:谢谢你教我一个问题的教训。以后我会努力把自己表达的更清楚。
  • 我很好奇std::map 是怎么做到的,因为它有extract()。使用 Apple 的 Clang STL,该节点拥有 std::pair&lt;const K, V&gt;。该节点有一个__ref(),它返回一个std::pair&lt;K&amp;, V&amp;&gt;,它通过const_cast 生成密钥。虽然 en.cppreference.com/w/cpp/container/node_handlenode_handle 是 UB “如果 std::pair 或 std::pair 存在用户定义的 std::pair 特化”,(为我怀疑是整对投射),我的实现似乎避免了这种情况。

标签: c++ stl constants const-cast std-pair


【解决方案1】:

这个可以通过reinterpret_cast解决,纯属编译时指令。

std::pair<int, double> p1 = { 1, 2.3 };
auto& p2 = reinterpret_cast<std::pair<const int, double>&>(p1);    // type of p2 is pair<const int, double>&

//++(p2.first);    // error: expression must be a modifiable lvalue
++(p1.first);
assert(p2.first == 2);


但是,reinterpret_cast 是一种危险的行为。我建议添加一个静态断言,以防止意外的部分模板特化。

using srcType = std::pair<Key, Value>;
using tarType = std::pair<const Key, Value>;

static_assert(
    offsetof(srcType, first) == offsetof(tarType, first)
 && offsetof(srcType, second) == offsetof(tarType, second)
);

【讨论】:

    【解决方案2】:

    我遇到了完全相同的问题。我的解决方案是创建一个新的地图对象作为迭代器类的一部分,然后将上游地图类中缺少的成员添加到其中,并返回对其成员的引用。效率不高,但很有效。

    您的解决方案有两个问题:

    1. 使用 const_cast 分配 const 变量是未定义的行为。编译器优化可能会产生奇怪的结果。
    2. 任何新的取消引用都会使先前取消引用的结果无效。它不应该。因此,根据迭代器的使用情况,它也可能会产生奇怪的结果。

    【讨论】:

    • 但就我而言,没有上游地图对象(它是一个自定义容器类,不包含可以引用的 std::pair 元素。跨度>
    • 不过,您可以在每个迭代器以一种或另一种方式取消引用时创建内部临时 std::pair 元素,并在迭代器实例销毁时销毁它们。
    【解决方案3】:

    您可以将pair&lt;Key,Value&gt; 类型的 转换为pair&lt;const Key,Value&gt;

    但是,仔细阅读这个问题,您实际上是在问,如果给定 pair&lt;Key,Value&gt;,您是否可以创建一个 pointerreferencepair&lt;const Key,Value&gt;,指同一个对象。

    答案是否定的 - 一个类型的引用或指针可以引用不同类型的对象的唯一情况是对象类型继承自被引用类型。

    一种可能性是返回一对引用,pair&lt;const Key&amp;, Value&amp;&gt;,它是根据您希望引用的对创建的。

    【讨论】:

    • 谢谢,我会看看引用对解决方案是否能解决我的特殊问题(感谢您的仔细阅读,我以后会尝试更清楚地表达自己) .
    【解决方案4】:

    正如 Kerrek SB 所指出的,您可以从 std::pair&lt;Key, Value&gt; 构造 std::pair&lt;const Key, Value&gt;。但是,您最初的问题意味着您希望避免在每次取消引用迭代器时构造 std::pair 对象。

    不幸的是,没有一个好的方法可以做到这一点。您可能必须构造对对象并将其实际存储在某个地方,特别是对于 operator->。否则,您必须能够让您的地图实际存储pair&lt;const Key, Value&gt;,以便能够从您的迭代器返回指向它的引用/指针。基本上要返回一个引用/指针,它必须以这种形式存储在某个地方:它不能是临时的。

    避免使用 const_cast。这只是在您使用它以这种方式强制转换配对时要求未定义的行为,即使它可能经常工作。

    【讨论】:

      【解决方案5】:

      是的。

      std::pair<int, double> p(1,2);
      std::pair<const int, double> q = p;   // no problem
      
      //q.first = 8;  // error
      q.second = 9;
      
      int b; double d;
      std::pair<int &, double &> s(b,d);
      std::pair<int const &, double &> t = s;  // also fine
      

      【讨论】:

      • 在我的特殊情况下,这将导致返回对临时对象的引用,导致编译失败。
      • @MarkusSaers:给定你的pair&lt;K,V&gt; p,你可以返回一个pair&lt;const K &amp;, V &amp;&gt;(p.first, p.second),它不会创建任何临时对象。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-06
      • 1970-01-01
      • 2019-08-08
      • 2015-03-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多