Forum: PC-Programmierung C++ referenzen auf lokale rückgabe-objekte


von Vlad T. (vlad_tepesch)


Lesenswert?

Hi, vielleicht kann mir jemand helfen:
ich habe ein C-Struktur (BS), die einen C++ Wrapper (B) bekommen soll, 
dem eine C-Instanz als Pointer gegeben wird.

Weiterhin gibt es eine Klasse (A), die als Member (unteranderem) die 
C-Struktur enthält.
Nach außen soll sie aber den Wrapper als Interface anbieten.
Einmal als const Object und einmal als veränderbares Objekt.

damit man dem const Wrapper nicht einfach kopiert und damit die nicht 
const-Routinen aufrufen kann, sind Kopie-C'tor und =-Operator privat, so 
dass nur A sie aufrufen kann.

Nun schlägt aber auch die Zuweisung der A.getB() an eine Referenz fehl, 
was mir nicht einleuchtet. Ich häte erwartet, dass das geht.

http://codepad.org/YcHXvtjj
> error: 'B::B(const B&)' is private


1
struct BS{
2
  int a;
3
  int b;
4
};
5
6
class B
7
{
8
public: 
9
  B(BS* bs)
10
   : m_bs(bs)
11
  {}
12
13
  int  getA() const {return m_bs->a;}
14
  void setA(int a)  {m_bs->a = a;}
15
16
private:
17
  BS* m_bs;
18
19
  B(const B& oth){ 
20
    this->m_bs = oth.m_bs;
21
  }
22
  B& operator=(const B& oth){ 
23
    this->m_bs = oth.m_bs;
24
    return *this;
25
  }
26
  friend class A;
27
};
28
29
30
31
class A
32
{
33
public:
34
  A(int a, int b)
35
  {
36
    m_bs.a = a;
37
    m_bs.b = b;
38
  }
39
40
  const B getB const ()
41
  { 
42
    return B( &(const_cast<A>(this)->m_bs)); // rückgabewert const, daher ist erzeugen von Wrapper ok
43
  }
44
45
  B getB4change()
46
  { 
47
    return B(&m_bs);
48
  }
49
50
51
private:
52
  BS m_bs;
53
};
54
55
int main()
56
{
57
  A a(10,11);
58
  // B b = a.getB(); // schlägt logischer weise fehl
59
  // const B& c = a.getB(); // <-warum schlägt dies fehl?
60
  // const B& d(a.getB());  // <-warum schlägt dies fehl?
61
62
  BS bs = {1,2};
63
  B ba(&bs);
64
  const B& bb = ba;         // dies schlägt erwartungsgemäß nicht fehl
65
}

von Karl H. (kbuchegg)


Lesenswert?

Vlad Tepesch schrieb:


>   // const B& c = a.getB(); // <-warum schlägt dies fehl?

Weil der Compiler hier an dieser Stelle ein sog. 'temporary Object' 
erzeugen muss, von dem er dann die Referenz bilden kann. getB liefert ja 
ein Objekt und da du dieses Objekt nicht selbst kopierst, muss der 
Compiler eine Kopie herstellen, weil ja das in der Funktion erzeugte 
Objekt ein Ablaufdatum hat.

Aber: selbst wenn er dieses temporary object in der Optimierung wieder 
los wird, entbindet das den Code an dieser Stelle nicht, dass der Copy 
Konstruktor accessible ist. Und da er das nicht ist, setzt es einen 
Fehler.

von Karl H. (kbuchegg)


Lesenswert?

Im übrigen wär dir das sowieso abgestürzt.

Denn dieses B Objekt, welches die Funktion retourniert wird ja nach der 
Anweisung zerstört. Womit der Aufrufer mit einer Referenz zurückbleit, 
zu einem Objekt welches nicht mehr existiert.

von Maximilian (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Im übrigen wär dir das sowieso abgestürzt.
>
> Denn dieses B Objekt, welches die Funktion retourniert wird ja nach der
> Anweisung zerstört. Womit der Aufrufer mit einer Referenz zurückbleit,
> zu einem Objekt welches nicht mehr existiert.

Bzzzz, falsch.

Das temporäre Objekt lebt solange, wie die Referenz lebt.

von Rolf Magnus (Gast)


Lesenswert?

Maximilian schrieb:
> Karl Heinz Buchegger schrieb:
>> Im übrigen wär dir das sowieso abgestürzt.
>>
>> Denn dieses B Objekt, welches die Funktion retourniert wird ja nach der
>> Anweisung zerstört. Womit der Aufrufer mit einer Referenz zurückbleit,
>> zu einem Objekt welches nicht mehr existiert.
>
> Bzzzz, falsch.
>
> Das temporäre Objekt lebt solange, wie die Referenz lebt.

Etwas vereinfacht, aber im Prinzip richtig. Hier das ganze in 
Standard-Sprech:


— If the initializer expression is an rvalue, with T2 a class type, and 
“cv1 T1” is reference-compatible with “cv2 T2,” the reference is bound 
in one of the following ways (the choice is implementation-defined):

— The reference is bound to the object represented by the rvalue (see 
3.10) or to a sub-object within that object.

— A temporary of type “cv1 T2” [sic] is created, and a constructor is 
called to copy the entire rvalue object into the temporary. The 
reference is bound to the temporary or to a sub-object within the 
temporary.The constructor that would be used to make the copy shall be 
callable whether or not the copy is actually done. [Example:
1
struct A { };
2
struct B : public A { } b;
3
extern B f();
4
const A& rca = f();
5
 // Either bound to the A sub-object of the B rvalue,
6
// or the entire B object is copied and the reference
7
// is bound to the A sub-object of the copy
—end example]

von Vlad T. (vlad_tepesch)


Lesenswert?

int i = a.getB().getA();

geht allerdings.

Maximilian schrieb:
> Das temporäre Objekt lebt solange, wie die Referenz lebt.

genau das hätte ich erwartet.
Das in A::getB beim Return eine Kopie gemacht wird die Referenz 
gespeichert wird.

angenommen der Copy-C'tor wäre nicht private, dann hätte ich folgendes 
erwartet:
1
B  b  = a.getB();
2 Copy-C'tor Aufrufe
 - einen beim Return in der Klasse B
 - einen bei der Initialisierung von b im aufrufenden Code
1
B& br = a.getB();
1 Copy-C'tor Aufruf
 - einen beim Return in der Klasse B

Entsprechend dieser Annahmen hätte es mit dem private Copy-C'tor im 
zweiten Fall klappen sollen.

Beide Fälle scheinen aber identisch (wenn man jetzt mal Polymorphie aus 
dem Spiel lässt) und komplett anders als gedacht behandelt zu werden .

wohingegen
1
int i = a.getB().getA();
wie angenommen zu funktionieren scheint.
hier geschieht ein Copy-C'tor-Aufruf, der auch mit privatem copy c'tor 
funktioniert.


Rolf Magnus schrieb:
> Etwas vereinfacht, aber im Prinzip richtig. Hier das ganze in
> Standard-Sprech:

das bezieht sich doch aber auf Vererbungsbeziehungen und Polymorphie .

von Rolf Magnus (Gast)


Lesenswert?

> Rolf Magnus schrieb:
>> Etwas vereinfacht, aber im Prinzip richtig. Hier das ganze in
>> Standard-Sprech:
>
> das bezieht sich doch aber auf Vererbungsbeziehungen und Polymorphie .

Es bezieht sich auf die Initialisierung von Referenzen durch "rvalues" 
von Klassen-Typen, egal ob es sich um eine Referenz auf den selben Typ 
oder eine Basisklasse davon handelt. Daher ja auch:

> The reference is bound to the temporary

... wenn die Referenz vom selben Typ wie das Objekt ist ...

> or to a sub-object within the temporary.

... wenn sie von einer Basisklasse des Objekts ist.


Es steht ja auch nirgends in dem Text, daß “cv1 T1” und “cv2 T2” nicht 
derselbe Typ sein dürfen, sondern nur, daß sie "reference compatible" 
sein müssen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.