为什么 "struct with const member" 类型的指针不能指向 "struct with non-const member"?

Why can't a pointer of a "struct with const member" type point to a "struct with non-const member"?

此代码无法编译:

struct s_t {
    int a;
};

struct c_s_t {
    const int a;
};

s_t s;
c_s_t *c_s = &s;
ibug@ubuntu:~ $ g++ -fsyntax-only t.cpp
t.cpp:10:15: error: cannot convert ‘s_t*’ to ‘c_s_t*’ in initialization
 c_s_t *c_s = &s;
               ^

然而这个编译完美:

int a, *pa = &a, **ppa = &pa, ***pppa = &ppa;
const int * const * const * const cpcpcpa = pppa;

我知道在 any 级别上更符合 CV 限定的指针可以指向任何级别上不太符合 CV 限定的对象,但为什么不一样对于结构?


上面的问题陈述是一个更复杂问题的MCVE,我的朋友试图在t_s_t<T>t_s_t<const T>之间转换指针,其中t_s_t是模板结构类型有一个模板参数 typenameT 是任意类型。

这两个是不同的结构,它们不可转换。他们拥有相同成员这一事实无关紧要,即使您从 c_s_t 中删除 const,也不会改变任何内容。

您的另一个示例之所以有效,是因为您将修饰符应用于一种类型。例如,这是完全合法的:

struct s_t {
    int a;
};

s_t s;
const s_t const* cs = &s;

原因是s_tc_s_t是不同的类型

即使您将 c_s_t 定义为:

struct c_s_t {
    int a; // <-- non-const!
};

然后:

s_t s;
c_s_t *c_s = &s;

还是不行。

类型错误是您的问题,您只是在尝试分配错误的类型。

这可行:

s_t s;
s_t *c_s = &s; //types are matching

I understand that a pointer that is more CV-qualified at any level can point to a less CV-qualified object at any level

这实际上不是真的,至少不是你描述的那样。只有最顶层的 CV 限定符可以任意添加(当然,指针本身的 CV 限定符!),C 和 C++ 都是这种情况。

这是 "any level" 概念的反例,直接取自当前草案标准中的 [conv.qual/3]

[ Note: If a program could assign a pointer of type T** to a pointer of type const T** (that is, if line #1 below were allowed), a program could inadvertently modify a const object (as it is done on line #2). For example,

int main() {
  const char c = 'c';
  char* pc;
  const char** pcc = &pc;       // #1: not allowed
  *pcc = &c;
  *pc = 'C';                    // #2: modifies a const object
}

— end note ]

不管怎样,那你问:

but why isn't it the same for structures?

当然,您可以将 const T* 指向 T,但这不是您正在做的。此规则不适用于递归。 类 可以容纳多个成员,因此您的方法通常不起作用(并且不需要针对单个成员 类 的特殊规则)。

在这种特殊情况下,两个 类 是布局兼容的,所以我希望 reinterpret_cast 大多数时候都能正常工作:

struct s_t {
    int a;
};

struct c_s_t {
    const int a;
};

int main()
{
   s_t s;
   c_s_t *c_s = reinterpret_cast<c_s_t*>(&s);
}

(live demo)

但是,看起来 aliasing on the merits of layout-compatibility is not actually well-defined 所以最终你最好重新考虑你的设计。

tl;dr: 不同的类型是不同的类型。