【问题标题】:What value does const void * offer over void *?const void * 比 void * 提供什么价值?
【发布时间】:2012-07-09 01:20:04
【问题描述】:

在 C++ 中,使用 const void * 作为函数的参数类型而不是 void * 有什么价值吗?由于void * 是不透明的,除了用户执行reinterpret_cast 之外,是否存在任何修改风险,在这种情况下,他们同样可以在const void * 上执行const_cast,因此人们真的会买任何东西吗?我之所以问,是因为我使用了一个用于共享指针的实用程序模板类,它提供了对 void 的专门化以避免 void & 问题,但没有为 const void 提供专门化,因此我想知道这是否只是一个疏忽,还是应该永远需要吗?

【问题讨论】:

  • 不需要reinterpret_caststatic_cast 足以转换为任何其他类型。

标签: c++ constants void-pointers reinterpret-cast const-cast


【解决方案1】:

const 的所有用途一样,它有两个用途。在函数的实现上,它将帮助编译器检测误用,正如您所提到的,可以通过 const_cast(或 C 风格的转换)强制和消除误用。

但是const 有第二个目的,它提供了不会修改对象的承诺,并且这样做使用户能够将指针传递给 const 对象(假设您会遵守承诺),有效地启用更广泛地使用您的功能。从这个简单的例子可以看出这一点:

void foo( const void* );
void bar( void* );
int main() {
    const int value = 10;
    foo( &value );          // correct, the function promises not to modify the value
    //bar( &value );        // error, this would break const correctness
}

【讨论】:

  • bar 也被声明为接收const void*。我是否遗漏了一些关于为什么用&value 调用它会破坏 const 正确性的东西,或者这只是一个错字?
  • @DanF:睡眠剥夺,这就是不缺的。感谢您提请注意错字。 bar 函数旨在通过非常量 void * 获取参数
【解决方案2】:

memcpy 有两个指针参数,一个是void*,另一个是const void*。第二个参数可以从 const char*(或其他指向常量对象类型的指针)参数隐式转换,而第一个参数不能。

没有隐式转换是价值——它迫使用户在他们想要的(不太可能的)事件中故意丢弃 const,而不是意外丢弃它。

然后在 memcpy 或类似函数的实现中,程序员必须在尝试修改其引用之前 const_cast 或 C 样式转换 const void* 参数。他们将能够static_cast 非常量参数并修改其引用。你需要写的那种演员希望告诉你你正在做的事情是否明智。

我认为,如果您的 shared_ptr 辅助函数需要特别对待 void,那么他们需要特别对待所有 cv-qualified void。所以这是四种情况:voidconst voidvolatile voidconst volatile void。但是,如果这些功能的用户过去在shared_ptr<void> 上尝试过,并抱怨它不起作用,但从未在shared_ptr<const void> 上尝试过,那么可能没有出现问题。

也许shared_ptr<void> 已经很不寻常了,以至于它还没有出现。也许使用shared_ptr<void> 的那种人往往不介意抛弃 cv-qualifiers,因为每当有人最终恢复正确的类型时,他们也会恢复正确的限定符。

想一想——shared_ptr<const void> 是否有效,或者shared_ptr 中调用删除器的代码是否需要从T*void* 的隐式转换?我不记得我是否曾经使用过shared_ptr<const T>

【讨论】:

    【解决方案3】:

    它提供了与const 在其他指针类型上提供的相同的好处:除非您明确放弃const-ness,否则您无法修改指向的内容。在接口中,const void* 是客户端代码的标志,表明您传入的任何内容都可能被读取但不能写入。例如,std::memcpy 被声明为

    void *memcpy(void *dest, const void *src, std::size_t count);
    

    这表示它将读取src 并写入dest。当然,如果它真的是用 C++ 实现的(可能但不太可能),它必须将两个指针都转换为其他类型。

    如果你觉得这个“没有给你买任何东西”,那么显然是const关键字本身没有任何价值。

    【讨论】:

    • 没错,const 对 API 的用户来说非常有用。例如,只需在编写函数调用时在 IDE 中调出原型,然后告诉您哪些参数是可变的,就像在 memcpy() 中一样。
    • 此外,当调用者有一个 const 指针并且在声明的参数类型确实应该是 const 时必须抛弃 const-ness(以避免警告)时,这很烦人。
    【解决方案4】:

    “自我记录”代码仍有好处。

    store_pod(const void* data, std::size_t bytes);
    

    没有任何注释让您看到指向的数据不会被修改。

    另外,请注意,要打破 const 的承诺,函数需要同时执行 const_castreinterpret_cast

    【讨论】:

      【解决方案5】:

      是的,const 始终具有(某些)相同的优点:它记录内容不应该被改变的事实。

      想象一下下面的代码:

      int const object = some_value();
      some_function(&object);
      

      此调用仅在函数参数声明为 void const* 时编译,否则客户端将需要 const_cast 来丢弃常量。当然,我们既不希望客户有这种不便,也不希望他们对他们的数据撒谎(通过抛弃 constness)。

      【讨论】:

        【解决方案6】:

        不要忘记const 的“文档价值”。即使有人总是可以将其丢弃,const 用于指示不应通过指针更改所指向事物的原始意图。 const_cast(和 reinterpret_cast 就此而言)应始终谨慎使用,并应在必要时让程序员暂停。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-06-30
          • 1970-01-01
          • 2015-04-26
          • 1970-01-01
          • 2011-11-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多