在`typeid`代码中奇怪地使用`?:`

Joh*_*itb 60 c++ conditional-operator typeid micro-optimization

在我正在研究的其中一个项目中,我看到了这段代码

struct Base {
  virtual ~Base() { }
};

struct ClassX {
  bool isHoldingDerivedObj() const {
    return typeid(1 ? *m_basePtr : *m_basePtr) == typeid(Derived);
  }
  Base *m_basePtr;
};
Run Code Online (Sandbox Code Playgroud)

我从来没有见过这样的typeid用法.为什么它会做那种奇怪的舞蹈?:,而不仅仅是做typeid(*m_basePtr)?有什么理由吗?Base是一个多态类(带有虚拟析构函数).

编辑:在这个代码的另一个地方,我看到这个,它似乎是等价的"多余的"

template<typename T> T &nonnull(T &t) { return t; }

struct ClassY {
  bool isHoldingDerivedObj() const {
    return typeid(nonnull(*m_basePtr)) == typeid(Derived);
  }
  Base *m_basePtr;
};
Run Code Online (Sandbox Code Playgroud)

cur*_*guy 48

我认为这是一个优化!一个鲜为人知且很少(你可以说"从不")使用的特性typeid是引用typeid异常而不是通常的UB 的参数的空取消引用.

什么?你是认真的吗?你醉了吗?

确实.是.没有.

int *p = 0;
*p; // UB
typeid (*p); // throws

是的,即使是语言丑陋的C++标准,这也很丑陋.

OTOH,这在参数的任何地方都不起作用typeid,所以添加任何杂乱将取消这个"功能":

int *p = 0;
typeid(1 ? *p : *p); // UB
typeid(identity(*p)); // UB

对于记录:我没有在此消息中声称编译器在进行取消引用之前自动检查指针不为null必然是一件疯狂的事情.我只是说,这样做检查时提领是立即的说法typeid,而不是其他地方,是完全疯了.(也许是在一些草案中插入的恶作剧,从未删除过.)

对于记录:我没有在前面的"For the record"中声明编译器插入指针不为空的自动检查,以及在取消引用null时抛出异常(如在Java中) :通常,在null取消引用上抛出异常是荒谬的.这是编程错误,因此异常无济于事.要求断言失败.

  • 什么?你是认真的吗?你醉了吗? (4认同)
  • @curiousguy“值类别”是表达式是否为左值等的术语。可能的类别为{lvalue,xvalue,prvalue}。还有组合{glvalue,rvalue}。 (2认同)

Ben*_*son 5

我能看到的唯一效果是1 ? X : X将你X作为一个右值而不是普通,X这将是一个左值.这typeid()对于像数组(衰减到指针)这样的事情很重要,但我认为如果Derived知道它是一个类就不重要了.也许它是从左右边界确实重要的地方复制的?这将支持关于"货物崇拜节目"的评论

关于下面的评论,我做了一个测试,果然typeid(array) == typeid(1 ? array : array),所以从某种意义上说我错了,但我的误解仍然可以与导致原始代码的误解相匹配!

  • §5.16/ 4:"*如果第二个和第三个操作数是左值并且具有相同的类型,则结果属于该类型并且是左值.*" (10认同)
  • http://stackoverflow.com/questions/75538/hidden-features-of-c/302563#302563 :) (7认同)
  • 我认为Visual C++虽然弄错了(去挖掘Connect问题报告).啊,这是使用条件运算符进行(不正确)右值转换的示例:https://connect.microsoft.com/VisualStudio/feedback/details/279444/arrays-decay-into-pointers-in-conditional-operator (2认同)