我偶然遇到过这个句法结构,我想知道:
它往往看起来像这样:
struct SubType : public SomeSuperType {
SubType(int somthing) : SuperType(something), m_foo(*((FooType *)0))
{}
private:
FooType m_foo;
}
要清楚,代码有效。但目的是什么?没有那条线的m_foo
的状态是什么?
答案 0 :(得分:6)
此构造的目的是在您正式需要一个对象但不想或不能声明一个对象的情况下模拟一个类型为SomeType
的虚假未命名对象。它有其有效用途,不一定会导致未定义的行为。
一个典型的例子是确定某个类成员的大小
sizeof (*(SomeClass *) 0).some_member
或类似的decltype应用
decltype((*(SomeClass *) 0).some_member)
上述两个示例均未导致任何未定义的行为。在非评估的上下文中,像*(SomeClass *) 0
这样的表达式是完全合法且有效的。
您还可以在语言标准本身中看到此技术用于说明目的,如8.3.5 / 12
尾随返回类型对于更多类型最有用 在declarator-id之前指定复杂:
template <class T, class U> auto add(T t, U u) -> decltype(t + u);
而不是
template <class T, class U> decltype((*(T*)0) + (*(U*)0)) add(T t, U u);
观察在(*(T*)0) + (*(U*)0)
下如何使用decltype
表达式对类型+
和{{1}之间的二进制T
运算符的结果类型执行编译时预测}。
当然,再次,这些技巧仅在非评估的上下文中使用时才有效,如上所示。
有时它被用作“空引用”的初始化器,如
U
但这实际上跨越了合法的边界并产生了未定义的行为。
您在特定示例中的内容无效,因为它会尝试在评估的上下文中访问无效的“null左值”。
P.S。在C语言中,还有一个特殊的规范部分,即运算符SomeType &r = *(SomeType *) 0;
和&
相互抵消,这意味着*
有效且保证计算为空指针。但它没有扩展到C ++。
答案 1 :(得分:5)
这是做什么的?未定义的行为。
设计推理可能是什么?希望导致未定义的行为。没有其他理由。
答案 2 :(得分:1)
我认为这个例子不一定是UB。这取决于FooType
的定义。假设Foo
是一个空类,其构造函数可以执行某些操作:
class Foo {
public:
Foo() { std::cout << "Hey, world! A new Foo just arrived.\n"; }
// I think the default copy and assign constructors do nothing
// with an empty type, but just in case:
Foo(const Foo&) {}
Foo& operator=(const Foo&) { return *this; }
};
现在,假设我需要一个Foo,无论出于何种原因,我不想触发构造函数。这样做不会导致任何实际的解除引用,因为 operator *没有取消引用并且复制构造函数不使用它的引用参数:
Foo(*static_cast<Foo*>(0));