将变量传递给具有常量参数的函数

6
我很困惑。我看到代码是这样的时候
void fun(const char **p) { }
int main(int argc, char **argv)
{
   fun(argv);
   getchar();
   return 0;
}

而且它显示了错误:从'char **'到'const char **'的无效转换

但是当代码像这样时

void test(const char *p) { }
int main()
{
    char *c = new char('a');
    test(c);
}

然后就没有编译错误了。这两段代码有什么区别呢?为什么第二段代码可以将一个变量传递给带有常量参数的函数呢?

3
void fun(const char *const *p)是可行的,甚至void fun(char *const *p)也是可行的。要对指向指针的指针进行这样的转换,如果在左边添加了const,则还需要在其右边的所有位置上添加它。但是我不记得为什么规则是这样的。 - undefined
13
https://isocpp.org/wiki/faq/const-correctness#constptrptr-conversion - undefined
1
@joergbrech 有趣的常见问题,我知道这个问题的派生到基类版本。我之前没有意识到T到const T也存在类似的问题。 - undefined
非常有帮助!谢谢! - undefined
1个回答

5
指向指针的指针很奇怪。以这个例子为例。
const int i = 42;
int* p;
const int** pp = &p;  // safe?
*pp = &i;  // *pp refers to p, but has the type const int*
*p = 420;  // !!

显然,你不能仅通过分配指针来击败const,所以从T**const T**的转换是非法的。
但是等一下,为什么将 T* 转换为 const T* 在第一次是合法的呢?
对,这是因为每个 T 都可以被视为 const T。一个 const T 只是对 T 的限制。在 T 上的任何操作在 const T 上都是合法的。如果你将类型视为表示对象上合法操作的集合,那么 const T 就代表了 T 的子集。
然而,对指针的限制却产生了相反的效果。一个 const T* 并不是 T* 的子集。一个 T* 只能指向非常量的 T,而 const T* 可以指向任何 T。就指针本身的赋值而言,const T*T* 的超集,而不是相反。
那么为什么从 T** 转换为 const T** 是不合法的呢?因为 T* 并不是 const T* 的超集:并非所有的 T* 都可以被视为 const T*,正如开头的片段所示。
†但是如果你解引用指针,const T 再次成为 T 的子集,所以 const T*T* 并不是彼此的子集。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接