保存对Derived的引用的基类 [英] Base class holding a reference to Derived

查看:108
本文介绍了保存对Derived的引用的基类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想这样做:

  struct Derived; 

struct Base {
Derived const& m_ref;
Base(Derived const& ref):m_ref(ref){}
};

struct Derived:Base {
Derived():Base(* this){}
};但是我似乎得到不可靠的行为(以后使用时, m_ref

/ code>指向无效的东西Derived)。



是否允许从 *构造一个Derived的引用在类初始化之前



我知道在使用之前使用已初始化,但我不知道类的初始化的变化如何影响对它的引用(因为初始化它不会移动它在内存中...)。



我不确定要调用什么我想要做的,所以我的搜索信息上绘制了一个空白...







更新:
我无法使用一个简单的测试用例重现我的问题,所以它看起来像是可能好的(虽然我不能证明它,并仍然欢迎一个确定的答案)。
怀疑我的问题来自一个破碎的复制赋值运算符。这是另一回事了!



更新2
我的拷贝构造函数和拷贝分配运算符确实是怪的,现在这个似乎工作可靠。

解决方案

3.8 / 1说:



类型T
的对象的生命周期开始: - 具有正确
对齐和大小的存储对于类型T是
获得,以及 - 如果T是具有非平凡构造函数(12.1)的
类类型,则
构造函数调用已完成。 / p>

3.8 / 5说:


strong>在对象的生命周期开始之前,但在对象将占据的存储之后已经被分配
,或者在对象的生命周期结束之后,在对象占用的存储之前是
重用或释放,指向对象将要或所在的存储位置的任何指针
可以使用,但只能以有限的方式
。这样的指针指的是分配的存储空间(3.7.3.2),并且使用
指针,就像指针类型为void *一样。 这样的指针可以取消引用,但是
生成的左值只能以有限的方式使用,如下所述。


Below是3.8 / 6:


这样的左值指的是已分配的存储空间(3.7.3.2)使用不是
的左值的属性取决于它的值是明确定义的。


...然后一个你不能做的事情的列表。绑定到相同的引用,派生类型不在其中。



我在其他地方找不到可能使您的代码无效的任何其他地方。值得注意的是,尽管在8.3.2 / 4中有以下短语:


引用应初始化为
引用有效对象或功能。


似乎没有任何有效对象的定义。



因此,经过很多时间和结束,我必须得出结论,这是合法的






当然,这并不是说这是一个好主意! 它看起来仍然是一个糟糕的设计。



例如,如果您以后更改了您的基础构造函数,并且以下任何内容变得相关(再次从3.8 / 6开始):




  • lvalue用于访问非静态数据成员或调用对象的非静态成员函数

  • 转换(4.10)到基类类型的引用

  • 左值用作 static_cast 的操作数)(除非转换最终转为 char& unsigned char&

  • lvalue用作 dynamic_cast (5.2.7)的操作数或作为 typeid 的操作数。



...那么您的程序将未定义,编译器可能不会对此进行诊断!






随机观察





首先,9.3.2似乎离开了未定义的 ctor-initializer 中的类型。异常!



其次,在指针上设置的标准为3.8 / 5(不是我从3.8 / 6引用的那个列表)包括:


如果对象将是或是一个
非POD类类型,程序有
未定义的行为,如果[..]
指针隐式转换(4.10)
到指向基类类型的指针。


这使得以下无害的代码未定义:

  struct A {
A(A * ptr){}
};

struct B:A {
B():A(this){}
};

int main(){
B b;
}


I'd like to do this:

struct Derived;

struct Base{
    Derived const& m_ref;
    Base(Derived const& ref) : m_ref(ref){}
};

struct Derived: Base{
    Derived(): Base(*this){}
};

But I seem to get unreliable behaviour (when used later on, m_ref points to things that aren't valid Derived).

Is it permissible to construct a reference to Derived from *this before the class has been initialised?

I appreciate that it is not valid to use such a reference until it has been initialised, but I don't see how changes to the initialisation of a class can affect references to it (since initialising it doesn't move it around in memory...).

I'm not sure what to call what I'm trying to do, so my search for information on this has drawn a blank...



Update: I can't reproduce my problems with a simple test case, so it looks like it is probably okay (though I can't prove it, and would still welcome a definitive answer). Suspect my problems arose from a broken copy-assignment operator. That's another matter altogether though!

Update 2 My copy constructor and copy-assignment operators were indeed to blame, and now this seems to work reliably. Still interested in whether or not it is well-defined behaviour though.

解决方案

3.8/1 says:

The lifetime of an object of type T begins when: — storage with the proper alignment and size for type T is obtained, and — if T is a class type with a non-trivial constructor (12.1), the constructor call has completed.

3.8/5 says:

Before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that refers to the storage location where the object will be or was located may be used but only in limited ways. Such a pointer refers to allocated storage (3.7.3.2), and using the pointer as if the pointer were of type void*, is well-defined. Such a pointer may be dereferenced but the resulting lvalue may only be used in limited ways, as described below.

"Below" is 3.8/6:

Such an lvalue refers to allocated storage (3.7.3.2), and using the properties of the lvalue which do not depend on its value is well-defined.

...and then a list of things you can't do. Binding to a reference to the same, derived type is not among them.

I can't find anything elsewhere that might make your code invalid. Notably, despite the following phrase in 8.3.2/4:

A reference shall be initialized to refer to a valid object or function.

there doesn't seem to be any definition of "valid object" to speak of.

So, after much to-ing and fro-ing, I must conclude that it is legal.


Of course, that's not to say that it's in any way a good idea! It still looks like a bad design.

For example, if you later change your base constructor and any of the following become relevant (again from 3.8/6):

  • the lvalue is used to access a non-static data member or call a non-static member function of the object
  • the lvalue is implicitly converted (4.10) to a reference to a base class type
  • the lvalue is used as the operand of a static_cast (5.2.9) (except when the conversion is ultimately to char& or unsigned char&
  • the lvalue is used as the operand of a dynamic_cast (5.2.7) or as the operand of typeid.

...then your program will be undefined, and the compiler may emit no diagnostic for this!


Random other observations

I notice a couple of other interesting things whilst compiling this answer, and this is as good a place as any to share them.

First, 9.3.2 appears to leave the type of this in a ctor-initializer accidentally unspecified. Bizarre!

Second, the criteria set on a pointer by 3.8/5 (not the same list that I quoted from 3.8/6) include:

If the object will be or was of a non-POD class type, the program has undefined behavior if [..] the pointer is implicitly converted (4.10) to a pointer to a base class type.

I believe that this renders the following innocuous-looking code undefined:

struct A {
   A(A* ptr) {}
};

struct B : A {
   B() : A(this) {}
};

int main() {
   B b;
}

这篇关于保存对Derived的引用的基类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆