GCC 8.2.1和MSVC 19.20编译以下代码,但Clang 8.0.0和ICC 19.0.1无法这样做。
// Base class.
struct Base {};
// Data class.
struct Data { int foo; };
// Derived class.
struct Derived : Base, Data { int bar; };
// Main function.
int main()
{
constexpr int Data::* data_p{ &Data::foo };
constexpr int Derived::* derived_p{ data_p };
constexpr int Base::* base_p{ static_cast<int Base::*>(derived_p) };
return (base_p == nullptr);
}
Clang 8.0.0 的错误消息如下:
case.cpp:16:33: error: constexpr variable 'base_p' must be initialized by a constant expression
constexpr int Base::* base_p{ static_cast<int Base::*>(derived_p) };
~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
我注意到它在两种情况下可以用Clang编译:
括括括词 int 派生::* derived_p{ data_p };
替换为 constexpr int 派生::* derived_p{
是否应该编译括词表达式(使 Clang 和 ICC 失败的表达式)?
我认为最后一行根本不合法,constexpr
或不合法。
>
可以将指向基类成员的指针转换为指向派生类成员的指针,但不能执行相反操作。关于指向类实例本身的指针之间的转换,指向成员的指针转换是逆变的。这就是为什么你需要static_cast
来强制编译器接受此输入,即使Base
有一个int
数据成员,你可以用指向成员的指针来引用它(见下文2)。
这也是有意义的:一个< code>Derived是一个< code>Base,因此一个< code>Derived实例有一个其父< code>Base类的子对象。现在,一个指向成员的指针并不是真正的指针,它是一个偏移量,只能和一个指向实际实例的地址一起使用。< code>Base内的任何偏移量也是< code>Derived内的有效偏移量,但是< code>Derived内的某些偏移量不是< code>Base内的有效偏移量。
< code>Base没有< code>int数据成员。你希望如何使用这个指向成员的指针?它捕获的偏移量可能引用< code>Derived实例中的< code>Data子对象,但这在运行时应该是UB,在编译时应该是编译器错误。
因此,gcc
也应该拒绝该片段,叮当声
和icc
是正确的。
我相信GCC和MSVC是正确的,这段代码应该可以编译。
< code>data_p指向< code>Data的成员< code>foo。< code>derived_p通过指向成员转换[conv.mem]/2的隐式指针指向< code>Derived的< code>Data基类子对象的成员< code>foo。
来自 [静态施法]/12
类型为"指向cv1T
类型的D
成员的指针"的prvalue可以转换为类型为"指向cv2T
类型的B
成员的指针"的prvalue,其中B
是D
的基类,如果cv2与cv1具有相同的cv限定,或者cv限定大于cv1。[…]如果classB
包含原始成员,或者是包含原始成员的类的基类或派生类,则指向成员的结果指针指向原始成员。否则,行为未定义。[注意:虽然classB
不需要包含原始成员,但通过指向成员的指针执行间接的对象的动态类型必须包含原始成员;参见[expr.mptr.oper]。-结束注释]
正如@geza在他下面的评论中所指出的,类< code>Base是< code>Derived的基类,后者在其< code>Data基类子对象中包含原始成员< code>Data::foo(上面引文中的注释似乎是支持这种解释的进一步证据)。因此,用于初始化< code>base_p的< code>static_cast是格式良好的,并且具有定义良好的行为。从< code >派生对象的< code>Base基类子对象的角度来看,结果指针指向< code >派生对象的< code>Data::foo成员。
要初始化constexpr对象,需要一个常量表达式[dcl.constexpr]/9。我们的表达式(static_cast
的结果)是一个核心常量表达式,因为[expr.const]/2中没有其他内容。它也是一个常量表达式,因为它是一个满足[expr.const]/5中列出的所有约束的prvalue。
从参考资料中,我发现: 类模板std::function是一个通用多态函数包装器。函数的实例可以存储、复制和调用任何可调用的目标函数、lambda表达式、绑定表达式或其他函数对象,以及指向成员函数的指针和指向数据成员的指针。 我不明白为什么应该能够存储这样的指针,而且我以前从未听说过这个功能。 真的有可能吗,我错过了什么或者是留档中的错误? 在这种情况下,
关于指针的转换,C草案标准(N3337)有以下内容: 4.10指针转换 2“指向cv的指针”类型的右值(其中T是对象类型)可以转换为“指向cv的指针无效”类型的右值将“指向cv的指针”转换为“指向cv的指针无效”的结果指向类型为T的对象所在的存储位置的起点,就好像该对象是类型为T的派生度最高的对象(1.8)(即,不是基类子对象)。 和 4.12布尔转换 1算术、枚举、指针或指向成员类型的指针的右值
所以我的教授喜欢用我们应该弄清楚的论文上的代码来测试我们。我不会发布代码,因为我不是在寻找答案,这只是他曾经让我们感到困惑的一件事是,当他初始化一个类的对象,然后初始化另一个类的指针指向上述对象时。它让我感到困惑,这是一个如此具体的问题,我不知道如何搜索它。例: “d- 当指针来自一个类却指向另一个类的对象时,它到底在做什么?我知道这个问题以前可能已经被回答过了,但是我在任何地方都找不到它,所以我
假设有一个结构 我得到一个指向该结构的成员的指针,比如作为某个函数的参数: 如何获取指向包含对象的指针?最重要的是:在不违反标准中的某些规则的情况下,也就是说,我想要标准定义的行为,而不是未定义或实现定义的行为。 附带说明:我知道这规避了类型安全。
我正在学习如何在C中使用并写了以下示例: 问:是否保证在所有情况下指向一个结构的指针都是指向它的第一个元素的完全相同的指针? 在这种特殊的情况下,它能像我预期的那样工作,但我不确定它是否能得到保证。编译器可以在开始时插入一些填充吗? 我唯一能找到的关于结构类型布局的是N1570的类型: 结构类型描述了一组按顺序分配的非空成员对象(在某些情况下,还包括一个不完整的数组),每个对象都有一个可选的指定名
我遇到了一个语法问题。看另一个StackOverflow答案并不能给出一个适用于我的问题的答案。至少不是我能理解的。 我的计划程序类: 这一切都编译得很好,但问题在于调用该函数中的成员函数指针: 编译这个会给我带来错误; 其他尝试: 有人能帮我解释一下我缺了什么知识吗?