【问题标题】:How can a const pointer refering to mutable data can be 'converted back' to non-const pointer?如何将对可变数据的 const 指针引用“转换回”为非常量指针?
【发布时间】:2020-03-13 22:40:12
【问题描述】:

我正在重构一个编译器前端,并面临 const 变量的问题,该变量对于它们的非 const 对应项是不可逆的。

上下文如下:我有一个抽象语法树 (AST) 和一个包含符号的符号表,我想计算符号表中 ast 节点和符号的类型。 我将类型计算部分称为“子系统”。

约束如下:

  • 我的 AST 是一个复杂的结构,所以我想避免每次都遍历所有节点。
  • 子系统是按照功能范式构建的,所以它不会直接改变任何东西(既不是 AST 也不是符号表),而只是使用符号表计算一个类型并最终计算一个符号。它的参数是当前需要计算的 AST 节点和符号表。
  • 当子系统计算完成后,我想将返回的符号引用为非常量指针。

这是我所说的(非常简化的)版本:

#include <string>
#include <map>

// For readability / understandability only
using Type = int;
using Symbol = int;

struct TypeSymbol {
    Type t;
    const Symbol* s;
};

TypeSymbol ComputeTypeSymbolInSubsystem(const std::map<std::string, int>& data) {

    return { 123, &data.at("bar") };
}

int main()
{
    std::map<std::string, Symbol> symbols = { {"test", 0 } , { "foo", 51 }, { "bar", 63 }, {"", 2 } };

    auto typeSymbol = ComputeTypeSymbolInSubsystem(symbols);

    // Obvious compilation error, even if "s" is still refering to a mutable memory zone (the symbols variable)
    *typeSymbol.s = 1;

    return 0;
}

在 gcc 9.2.0 下导致以下编译错误:

main.cpp: In function 'int main()':
main.cpp:25:19: error: assignment of read-only location '* typeSymbol.TypeSymbol::s'
   25 |     *typeSymbol.s = 1;
      |     ~~~~~~~~~~~~~~^~~

问题是:由于符号表和 AST 被传递给子系统“const”,现在不可能从返回的 TypeSymbol 的值中获取 const 指针。

在这种情况下,我怎样才能回到非常量指针?我考虑过使用 const_cast,但它通常意味着设计缺陷,不是吗?

【问题讨论】:

  • const_cast 并不意味着设计缺陷,而是将只读数据视为可变数据。
  • 您了解 const-pointer-to-non-const-data 与 nonconst-pointer-to-const-data 之间的区别吗?您正在处理后者,这就是您的代码无法编译的原因。您正在尝试通过指向常量数据的指针来修改数据,这意味着该指针不能用于写入。如果您知道指向的内存是可写的,那么是的,您可以const_cast 指针以允许写入数据。否则,您需要重新考虑使用const
  • @RemyLebeau 是的,我愿意,编译失败只是显示我实际系统设计问题的超简化版本的一种方式。我知道错误,但不是正确的解决方案,除了这个 const_cast。

标签: c++ architecture constants


【解决方案1】:

使用const_cast 是您的一种选择。

*const_cast<Symbol*>(typeSymbol.s) = 1;

但请记住,有时它可能很危险。

如果将原始对象创建为 const 对象,则存在未定义行为的风险。

【讨论】:

  • 谢谢。实际上,我 100% 确定我的原始对象是可变的,因为只有我的类 SymbolTable 被允许构建 Symbol(使用私人朋友构造函数)。我不是 const_cast 的忠实粉丝,但如果它是唯一可行的解​​决方案,我必须接受它!
  • @Scorbutics,另一种选择是从s的声明中删除const -- Symbol* s;
  • @R Sahu ,可悲的是,如果我这样做,我还必须在所有地方删除 const ,因为编译错误会变成:main.cpp: In function 'TypeSymbol ComputeTypeSymbolInSubsystem(const std::map&lt;std::__cxx11::basic_string&lt;char&gt;, int&gt;&amp;)': main.cpp:14:30: error: invalid conversion from 'const mapped_type*' {aka 'const int*'} to 'Symbol*' {aka 'int*'} [-fpermissive] 14 | return TypeSymbol { 123, &amp;data.at("bar") }; | ^~~~~~~~~~~~~~~ | | | const mapped_type* {aka const int*}
  • @Scorbutics 如果您将const 留在需要的位置,则在分配指向s 的指针时只需const_cast,而不是const_cast'ing 在通过以下方式写入内存时s,例如:TypeSymbol ComputeTypeSymbolInSubsystem(const std::map&lt;std::string, int&gt;&amp; data) { return { 123, &amp;const_cast&lt;Symbol&amp;&gt;(data.at("bar")) }; } 如果您打算通过s 写入内存,则绝对不应将s 声明为指向const 内存。但实际上,如果数据本来就是可变的,为什么 ComputeTypeSymbolInSubsystem() 会引用 const map
  • 因为在实践中,ComputeTypeSymbolInSubsystem 是一个多文件系统(大约 10 个文件),如果代码总共有大约 1000 行。而且我想确保我不能对其进行任何修改以保持功能性的做事方式,并避免在很多地方编辑符号表。分配指针时的 const_cast 也可能是一个好主意。我会考虑的!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-07-18
  • 1970-01-01
  • 1970-01-01
  • 2017-05-15
  • 1970-01-01
  • 2013-11-23
相关资源
最近更新 更多