【问题标题】:Is it legal to call a non-static member function of a stateless class on a nullptr? [duplicate]在 nullptr 上调用无状态类的非静态成员函数是否合法? [复制]
【发布时间】:2020-05-17 08:02:56
【问题描述】:

考虑以下代码:

int main()
{
    struct EmptyStruct{
        void nonstatic_mf() const { std::cout <<"EmptyStruct\n"; }
    };


    EmptyStruct *esptr = nullptr;
    esptr->nonstatic_mf(); 
}  

这是一个合法的 C++(它似乎在 gcc 和 clang 中工作)?

【问题讨论】:

  • 更有趣的问题是静态方法:-)
  • 添加像if (this != nullptr) ... 这样的代码也不会很好地结束,因为对于优化的代码,编译器知道this 永远不能为nullptr,所以代码就像if (true) ...。未定义的行为:不好。打开所有编译器警告是对抗未定义行为的一种方法。
  • @Jarod42 要我问吗?
  • 因为简单地回答[expr.post]/2 不是那么有趣...
  • @YSC 你已经回答了我的问题!非常感谢

标签: c++ pointers language-lawyer member-functions


【解决方案1】:

即使结构是空的,它的大小也不为零。必须有一些内存作为它的存储空间。

没有。这始终是 UB。如果您不需要实例,请将其设为静态。静态函数仍然可以使用点 . 语法调用。

为什么?因为您不能取消引用空指针。调用与此等效的成员函数:

 EmptyStruct *esptr = nullptr;
 (*esptr).nonstatic_mf();

如你所见,空指针是被引用的,也就是UB。

标准对此有何规定?来自[class.mfct.non-static]/2

如果为非 X 类型或从 X 派生的类型的对象调用类 X 的非静态成员函数,则行为未定义。

空指针不指向EmptyStruct 的有效实例。仅此一项就足以使行为未定义

来自[expr.ref]/2

对于第一个选项(点),第一个表达式应该是一个左值。 对于第二个选项(箭头),第一个表达式应是具有指针类型的纯右值。 表达式E1-&gt;E2 转换为等价形式(*(E1)).E2[expr.ref] 的其余部分将仅处理第一个选项(点)。

所以esptr-&gt;nonstatic_mf() 实际上等同于(*esptr).nonstatic_mf(),并且取消引用空指针是未定义的行为。

因此,此代码有两种未定义的方式。

【讨论】:

  • 我猜这里的“或多或少”是关键部分。我会天真地说成员函数的参数是一个指针(不需要取消引用任何东西),但我不现在标准是怎么说的,所以我在问。
  • 好的,谢谢,我将其标记为答案。我只是在想,也许成员函数是由指针(而不是引用)调用的,并且内置箭头比点更基本(即使点不能重载)。但情况恰恰相反。
  • @Adrian 是的。 this 从一开始就应该是一个引用,因为它不能以任何格式正确的方式为空。当启用优化时,编译器甚至会删除诸如 if (this == nullptr) 之类的检查。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-18
  • 2014-04-14
  • 2015-02-27
  • 1970-01-01
相关资源
最近更新 更多