Forum: PC-Programmierung class rvalue used as lvalue


von Mixer (Gast)


Lesenswert?

VC++ zeigt Warnung C4238 wenn ich bei Aufruf der Funktion 
UpdateAllViews() die Adresse der Klassen-Variable CHintCenter, wie im 
Code zu sehen, übergebe. Also ohne vorherige Definition einer Variablen 
vom Typ CHintCenter. Das funktioniert so, aber kann ich das anders 
schreiben, ohne eine zusätzliche Variable, damit die Warnung nicht 
kommt? Ähnliche Konstrukte habe ich sehr viele und möchte den 
Schreibaufwand einer Variablendefinition vermeiden.
1
class CHintCenter : public CObject
2
{
3
public:
4
  CHintCenter(float fLat = 0.0f, float fLon = 0.0f)
5
    : m_fLat(fLat), m_fLon(fLon) { }
6
7
  float m_fLat, m_fLon;
8
};
9
10
11
pDoc->UpdateAllViews(NULL, HINT_UPDATE_CENTER, &CHintCenter(fLat, fLon));

von Oliver S. (oliverso)


Lesenswert?

Von den gefühlt 2 zillionen Beiträgen im Netz zum Thema sollten die 
beiden hier eigentlich alles auf den Punkt bringen:

https://stackoverflow.com/questions/8763043/how-to-get-around-the-warning-rvalue-used-as-lvalue?rq=1


https://msdn.microsoft.com/en-us/library/7zyb9yb4.aspx

Oliver

von Mixer (Gast)


Lesenswert?

Leider finde ich nur die Antwort, es gibt keine elegante Lösung. Meine 
Hoffnung ist aber, in all dem Firlefanz den man in den aktuellen C++ 
Standard gepackt hat, findet sich etwas Passendes.

von Oliver S. (oliverso)


Lesenswert?

Die elegante Lösung steht im zweiten Beitrag, ist die Option /ZE, und 
das Gottvertrauen, daß du weißt, was du tust.

Alternativ schreib ein #pragma warning (disable : 4238) davor.

Oliver

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Mixer schrieb:
> VC++ zeigt Warnung C4238 wenn ich bei Aufruf der Funktion
> UpdateAllViews() die Adresse der Klassen-Variable CHintCenter,

Im C++-Sprachgebrauch gibt es keine Klassen-Variablen (wäre z.B. 
Java-Speech). Meinst Du ein statisches Datenelement? Das wäre hier aber 
auch nicht der Fall.

> wie im
> Code zu sehen, übergebe. Also ohne vorherige Definition einer Variablen
> vom Typ CHintCenter. Das funktioniert so, aber kann ich das anders
> schreiben, ohne eine zusätzliche Variable, damit die Warnung nicht
> kommt? Ähnliche Konstrukte habe ich sehr viele und möchte den
> Schreibaufwand einer Variablendefinition vermeiden.

Suche mal nach temp. Objekte und Address-Operator ...

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Mixer schrieb:
> Ähnliche Konstrukte habe ich sehr viele und möchte den
> Schreibaufwand einer Variablendefinition vermeiden.

dann ändere die Signatur von UpdateAllViews(), sodass sie eine Referenz 
auf eine Konstante nimmt. (Kleiner Tipp, die Deklaration von CHintCenter 
hat mit dem "Problem" überhaupt nichts zu tun und eine Kopie der Warnung 
könnte auch nicht schaden):

> Meine Hoffnung ist aber, in all dem Firlefanz den man in den aktuellen C++
Standard gepackt hat, findet sich etwas Passendes.

Dann beschäftige Dich doch mal mit C++. Die Adresse eines temporaries zu 
nehmen, war noch nie gültiges C++.

von Dr. Sommer (Gast)


Lesenswert?

1
class CHintCenter : public CObject
2
{
3
public:
4
  CHintCenter(float fLat = 0.0f, float fLon = 0.0f)
5
    : m_fLat(fLat), m_fLon(fLon) { }
6
7
  float m_fLat, m_fLon;
8
};
9
10
template <typename T, typename... Args>
11
void updateAllViews (CDocument* pDoc, CView* pSender, LPARAM lHint, Args&&... args) {
12
  T objHint (std::forward<Args> (args)...);
13
  pDoc->UpdateAllViews (pSender, lHint, &objHint);
14
}
15
16
updateAllViews<CHintCenter>(pDoc, NULL, HINT_UPDATE_CENTER, fLat, fLon);


Oder falls kopieren erlaubt ist:
1
class CHintCenter : public CObject
2
{
3
public:
4
  CHintCenter(float fLat = 0.0f, float fLon = 0.0f)
5
    : m_fLat(fLat), m_fLon(fLon) { }
6
  CHintCenter (const CHintCenter&) = default;
7
  float m_fLat, m_fLon;
8
};
9
10
template <typename T>
11
void updateAllViews (CDocument* pDoc, CView* pSender, LPARAM lHint, T obj) {
12
  pDoc->UpdateAllViews (pSender, lHint, &obj);
13
}
14
15
updateAllViews(pDoc, NULL, HINT_UPDATE_CENTER, CHintCenter { fLat, fLon });

Oder
1
class CHintCenter : public CObject
2
{
3
public:
4
  CHintCenter(float fLat = 0.0f, float fLon = 0.0f)
5
    : m_fLat(fLat), m_fLon(fLon) { }
6
7
  float m_fLat, m_fLon;
8
  void updateAllViews (CDocument* pDoc, CView* pSender, LPARAM lHint) {
9
    pDoc->UpdateAllViews (pSender, lHint, this);
10
  }
11
};
12
13
CHintCenter { fLat, fLon }.updateAllViews (pDoc, NULL, HINT_UPDATE_CENTER);

von Dr. Sommer (Gast)


Lesenswert?

PS: Länge und Breite als float? Ob das genau genug ist...?

von Mixer (Gast)


Lesenswert?

Ich hoffte, dass es etwas Lambda-Ausdruck Artiges gibt, mit dem sich 
eine kompakte Schreibweise realisieren lässt. Der Zweizeiler
1
CHintCenter HintCenter(fLat, fLon);
2
pDoc->UpdateAllViews(NULL, HINT_UPDATE_CENTER, &HintCenter);
soll doch schrumpfen.

Dr. Sommer schrieb:
> PS: Länge und Breite als float? Ob das genau genug ist...?

Lat/Lon sind als lokalisierender float Wert genau genug. Nur bei der 
Berechnung mit Winkelfunktionen muss man zu double übergehen.

von Wilhelm M. (wimalopaan)


Lesenswert?

Mixer schrieb:
> Ich hoffte, dass es etwas Lambda-Ausdruck Artiges gibt, mit dem sich
> eine kompakte Schreibweise realisieren lässt. Der Zweizeiler
>
1
> CHintCenter HintCenter(fLat, fLon);
2
> pDoc->UpdateAllViews(NULL, HINT_UPDATE_CENTER, &HintCenter);
3
>
> soll doch schrumpfen.

Das Problem liegt in der Signatur von UpdateAllViews(...). 
Wahrscheinlich ist die Verwendung eines rohen Zeigers auf ein lokales 
Objekt eh falsch bzw. unsinnig. Daher die Frage: liegt die Funktion in 
Deinem Einflussbereich, um sie zu ändern?

von Mixer (Gast)


Lesenswert?

Man muss nur hartnäckig sein, es gibt doch eine triviale Lösung die 
Compiler Warnung zu vermeiden.

Diese Variante erzeugt Warnung C4238.
1
class CHintCenter : public CObject
2
{
3
public:
4
  CHintCenter(float fLat = 0.0f, float fLon = 0.0f)
5
    : m_fLat(fLat), m_fLon(fLon) { }
6
7
  float m_fLat, m_fLon;
8
};
9
10
pDoc->UpdateAllViews(NULL, HINT_UPDATE_CENTER, &CHintCenter(fLat, fLon));
Diese Variante funktioniert ohne Warnung.
1
class CHintCenter : public CObject
2
{
3
public:
4
  CHintCenter(float fLat = 0.0f, float fLon = 0.0f)
5
    : m_fLat(fLat), m_fLon(fLon) { }
6
7
  operator CHintCenter*() { return this; }
8
9
  float m_fLat, m_fLon;
10
};
11
12
pDoc->UpdateAllViews(NULL, HINT_UPDATE_CENTER, CHintCenter(fLat, fLon));

von Dr. Sommer (Gast)


Lesenswert?

Mixer schrieb:
> Diese Variante funktioniert ohne Warnung.

Falsch ist die trotzdem ( Pointer auf temporaries).

von Mixer (Gast)


Lesenswert?

Dr. Sommer schrieb:
> Falsch ist die trotzdem ( Pointer auf temporaries).

Ein Sakrileg, an der Weisheit der Compiler-Bauer zu zweifeln.

von Dr. Sommer (Gast)


Lesenswert?

Mixer schrieb:
> Ein Sakrileg, an der Weisheit der Compiler-Bauer zu zweifeln.

Nur weil ein Programm ohne Warnungen kompiliert, ist es noch lange nicht 
korrekt. Es ist unmöglich einen Compiler zu bauen, der alle Probleme 
findet ( Satz von Rice).

von Wilhelm M. (wimalopaan)


Lesenswert?

Solche Warnungen des Compilers sind auch wirklich lästig. Auch die 
Ratschläge, dass Problem verstehen zu lernen, sidn ja voll nervig.

Deswegen folgender Tipp für Dein Problem und ähnliche Probleme in der 
Zukunft: vor dem fraglichen Codeabschnitt:
1
#ifdef _MSC_VER
2
#  pragma warning(push)
3
#  pragma warning(disable: 4238)
4
#endif

und danach:
1
#ifdef _MSC_VER
2
#  pragma warning(pop)
3
#endif

Aber nur, wenn Du magst ...

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Wilhelm M. schrieb:

> Deswegen folgender Tipp für Dein Problem und ähnliche Probleme in der
> Zukunft: vor dem fraglichen Codeabschnitt:

die Abfrage, ob es sich um den MSVC handelt, kannst Du Dir sparen, der 
Code ist so eh nicht portabel.

von tictactoe (Gast)


Lesenswert?

Mixer schrieb:
> Ähnliche Konstrukte habe ich sehr viele und möchte den
> Schreibaufwand einer Variablendefinition vermeiden.
> class CHintCenter : public CObject
> {
> public:
>   CHintCenter(float fLat = 0.0f, float fLon = 0.0f)
>     : m_fLat(fLat), m_fLon(fLon) { }
>
>   float m_fLat, m_fLon;
> };
>
> pDoc->UpdateAllViews(NULL, HINT_UPDATE_CENTER, &CHintCenter(fLat, fLon));

Also wenn schon eine eigen Klasse notwendig ist, die nur zu diesem Zweck 
existiert, dann schlage ich einen Cast-Operator vor:
1
class CHintCenter : public CObject
2
{
3
public:
4
  CHintCenter(float fLat = 0.0f, float fLon = 0.0f)
5
    : m_fLat(fLat), m_fLon(fLon) { }
6
7
  float m_fLat, m_fLon;
8
  using ObjPtr = CObject*;
9
  operator ObjPtr() { return this; }
10
};
11
pDoc->UpdateAllViews(nullptr, HINT_UPDATE_CENTER, CHintCenter(fLat, fLon));

von tictactoe (Gast)


Lesenswert?

Ups! Da war ich jetzt doch etwas spät dran und hab vor lauter Hickhack 
die fast gleich Lösung vorher übersehen.

von mh (Gast)


Lesenswert?

Mixer schrieb:
> operator CHintCenter*() { return this; }

tictactoe schrieb:
> operator ObjPtr() { return this; }

Mal davon abgesehen das eure Lösung falsch ist, glaubt ihr wirklich, 
dass es eine user-defined conversion function in diesem Fall eine gute 
Idee ist?

von Carl D. (jcw2)


Lesenswert?

In einem Fall wird eine Adresse eines temporären Objektes auf dem Stach 
übergeben, im anderen das Objekt selbst (per value).
Im letzteren Fall hat der Compiler die gesamte Lebensdauer des Objekts 
unter Kontrolle. Sobald man innerhalb der gerufenen Funktion beginnt die 
Adresse des Parameters weiterzureichen, sollte eigentlich dann wieder 
die Warbubg kommen.

von Wilhelm M. (wimalopaan)


Lesenswert?

Torsten R. schrieb:
> Wilhelm M. schrieb:
>
>> Deswegen folgender Tipp für Dein Problem und ähnliche Probleme in der
>> Zukunft: vor dem fraglichen Codeabschnitt:
>
> die Abfrage, ob es sich um den MSVC handelt, kannst Du Dir sparen, der
> Code ist so eh nicht portabel.

Ok, mag sein ... das kann ich an dem Codeschnipsel nicht beurteilen bzw. 
sehe dort keinen Grund für fehlende Portabilität.

Andere Compiler als der MSVC++ würden dann wiederum Warnungen wegen 
"unknown pragma" ausgeben.

: Bearbeitet durch User
von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Wilhelm M. schrieb:
> Ok, mag sein ... das kann ich an dem Codeschnipsel nicht beurteilen bzw.
> sehe dort keinen Grund für fehlende Portabilität.

Weil es in der Sprache C++ nicht erlaubt ist, die Adresse eines 
temporären Objektes zu nehmen. Alle anderen Compiler werden das als 
Fehler ablehnen.

von tictactoe (Gast)


Lesenswert?

mh schrieb:
> Mixer schrieb:
>> operator CHintCenter*() { return this; }
>
> tictactoe schrieb:
>> operator ObjPtr() { return this; }
>
> Mal davon abgesehen das eure Lösung falsch ist, glaubt ihr wirklich,
> dass es eine user-defined conversion function in diesem Fall eine gute
> Idee ist?

Also, man muss das Ganze schon ein bisschen pragmatischer und im Kontext 
sehen. Pauschalaussagen wie "Pointer auf temporäre Objekte sind um jeden 
Preis zu vermeiden" bringen einen bald an die Grenzen.

In diesem Fall handelt es sich um die Funktion 
CDocument::UpdateAllViews(), und man muss schon wissen, was die macht. 
Konkret legt sie nur eine sehr, sehr vage Bedeutung für den 2. und 3. 
Parameter fest. Es bleibt also in der Verantwortung des Verwenders, wie 
mit denen umgegangen wird.

Und wenn Mixer nicht völlig daneben ist, dann passiert mit dem 
durchgereichten Pointer nichts anderes als dass er wieder in 
CHintCenter* gecastet wird, die beiden Members ausgelesen werden, und 
dann der Pointer nie wieder angeschaut wird.

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

tictactoe schrieb:

> Also, man muss das Ganze schon ein bisschen pragmatischer und im Kontext
> sehen. Pauschalaussagen wie "Pointer auf temporäre Objekte sind um jeden
> Preis zu vermeiden" bringen einen bald an die Grenzen.

Es gäbe aber eine ganz pragmatische und "korrekte" Lösung: Einfach die 
Paramter per const ref nehmen. Wenn man die Funktion selbst nicht ändern 
kann, dann wäre es ein ganz pragmatischer Einzeiler, da eine Funktion zu 
schreiben:
1
void UpdateAllViews( CDocument& doc, CView* pSender, LPARAM lHint, const CHintCenter& hint )
2
{
3
   doc.UpdateAllViews( pSender, lHint, &hint );
4
}
5
6
UpdateAllViews(*pDoc, NULL, HINT_UPDATE_CENTER, CHintCenter(fLat, fLon));

Gültiges C++, keine Warning, keine gefährliche Konvertierungen.

von Dr. Sommer (Gast)


Lesenswert?

Torsten R. schrieb:
> Gültiges C++, keine Warning, keine gefährliche Konvertierungen.

Der Pointer ist dann const. UpdateAllViews möchte aber einen nicht const 
Pointer. Ich hab doch oben 3 korrekte Lösungen genannt, was ist schlecht 
an denen?

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Dr. Sommer schrieb:
> Torsten R. schrieb:
>> Gültiges C++, keine Warning, keine gefährliche Konvertierungen.
>
> Der Pointer ist dann const. UpdateAllViews möchte aber einen nicht const
> Pointer.

Ja, stimmt. Dann eben eine Kopie.

> Ich hab doch oben 3 korrekte Lösungen genannt, was ist schlecht
> an denen?

Überhaupt nichts (ich würde kein Template nehmen, da die anschließende 
Konvertierung nach void* das Typsystem umgeht; ich würde eher einen Satz 
überladener Funktionen nehemen)!

Frage bitte nicht mich, warum hier einige sich gegen korrekte Lösungen 
wehren und sich unbedingt mit inpliziten Konvertierungen in den Fuß 
schießen wollen. Ich wollte auf das Stichwort "Pragmatisch" hin, nur 
noch mal aufzeigen, dass das konkrete Problem wirklich einfach, robust 
und korrekt zu lösen ist.

von Dr. Sommer (Gast)


Lesenswert?

Torsten R. schrieb:
> da die anschließende Konvertierung nach void* das Typsystem umgeht;

Wo wird denn nach void* konvertiert? Laut Internet will UpdateAllViews 
ein CObject *. Das template hilft wenn man verschiedene Varianten mit 
verschiedenen Parameter Typen hat.

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Dr. Sommer schrieb:
> Torsten R. schrieb:
>> da die anschließende Konvertierung nach void* das Typsystem umgeht;
>
> Wo wird denn nach void* konvertiert? Laut Internet will UpdateAllViews
> ein CObject *. Das template hilft wenn man verschiedene Varianten mit
> verschiedenen Parameter Typen hat.
1
template <typename T>
2
void updateAllViews (CDocument* pDoc, CView* pSender, LPARAM lHint, T obj) {
3
  pDoc->UpdateAllViews (pSender, lHint, &obj);
4
}
5
6
CHintCenter c{ fLat, fLon };
7
updateAllViews(pDoc, NULL, HINT_UPDATE_CENTER, &c);

Den Fehler würde der Compiler finden können, wenn updateAllViews() kein 
Template wäre.

von Dr. Sommer (Gast)


Lesenswert?

Torsten R. schrieb:
> Den Fehler würde der Compiler finden können, wenn updateAllViews() kein
> Template wäre.

Der Compiler würde hier machen dass ein CHintCenter ** nach CObject * 
konvertiert wird

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Dr. Sommer schrieb:
> Torsten R. schrieb:
>
> Der Compiler würde hier machen dass ein CHintCenter ** nach CObject *
> konvertiert wird

Genau und dem OP folgend, wäre das wohl nicht gewünscht und ein Fehler. 
Und wir machen doch alle C++, weil wir wollen, dass der Compiler unsere 
Fehler möglichst früh findet.

von Dr. Sommer (Gast)


Lesenswert?

Torsten R. schrieb:
> Und wir machen doch alle C++, weil wir wollen, dass der Compiler unsere
> Fehler möglichst früh findet.

Naja ob der Compiler den Fehler jetzt beim äußeren oder beim inneren 
Funktionsaufruf anzeigt...

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Dr. Sommer schrieb:

> Naja ob der Compiler den Fehler jetzt beim äußeren oder beim inneren
> Funktionsaufruf anzeigt...

In dem Fehlerfall, den ich skiziert habe, generiert der Compiler keinen 
Fehler.

von Dr. Sommer (Gast)


Lesenswert?

Torsten R. schrieb:
> In dem Fehlerfall, den ich skiziert habe, generiert der Compiler keinen
> Fehler.

Doch:

Dr. Sommer schrieb:
> Der Compiler würde hier machen dass ein CHintCenter ** nach CObject *
> konvertiert wird
Es sollte natürlich meckern und nicht machen heißen.  Danke 
Autokorrektur

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Dr. Sommer schrieb:
> Torsten R. schrieb:
>> In dem Fehlerfall, den ich skiziert habe, generiert der Compiler keinen
>> Fehler.
>
> Doch:

Ah, ich war davon ausgegangen, dass der letzte Parameter ein void* ist 
(nicht ein CObject*). Zum Glück muste ich mit MFC nie arbeiten.

von Wilhelm M. (wimalopaan)


Lesenswert?

Torsten R. schrieb:
> Wilhelm M. schrieb:
>> Ok, mag sein ... das kann ich an dem Codeschnipsel nicht beurteilen bzw.
>> sehe dort keinen Grund für fehlende Portabilität.
>
> Weil es in der Sprache C++ nicht erlaubt ist, die Adresse eines
> temporären Objektes zu nehmen. Alle anderen Compiler werden das als
> Fehler ablehnen.

Es sei denn, der potentielle, mutige GCC Nutzer benutzt so wie der TO 
den Schalter /Ze auch die Option -fpermissive ...

von Wilhelm M. (wimalopaan)


Lesenswert?

Warum sind pSender und (vor allem) pHint eigentlich Output-Parameter 
(also non-const):
1
void UpdateAllViews(
2
   CView* pSender,
3
   LPARAM lHint = 0L,
4
   CObject* pHint = NULL 
5
);

(Bitte entschuldigt, ich habe von MFC keine Ahnung.)

von Oliver S. (oliverso)


Lesenswert?

Vermutlich war const noch nicht erfunden, als die Grundlagen für die MFC 
zusammenengebastelt wurden...

Immerhin findet Google für das „Problem“ mit der Warnung Beiträge von 
1995...

Oliver

von Wilhelm M. (wimalopaan)


Lesenswert?

Oliver S. schrieb:
> Vermutlich war const noch nicht erfunden, als die Grundlagen für die MFC
> zusammenengebastelt wurden...
>
> Immerhin findet Google für das „Problem“ mit der Warnung Beiträge von
> 1995...
>
> Oliver

Wohl kaum: const existiert in C++ seit "C with Classes" (lt. Wikipedia 
seit 1981) und MFC ist lt. Wikipedia von 1992.

von Oliver S. (oliverso)


Lesenswert?

Natürlich. Aber sowas wie const correctness war in „C mit Classes“ lange 
Zeit nicht so wirklich ein Thema, was man ja auch an MFC deutlich sieht.

Unter MFC steckt ein in Windows-Unterbau, dessen Ursprünge bis zu MS-DOS 
zurückreichen, und den Bill Gates noch persönlich in seiner Garage 
programmiert hat (oder es als zumindest als seins verkauft hat)

Oliver

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Wilhelm M. schrieb:
> Oliver S. schrieb:
>> Vermutlich war const noch nicht erfunden, als die Grundlagen für die MFC
>> zusammenengebastelt wurden...
>>
>> Immerhin findet Google für das „Problem“ mit der Warnung Beiträge von
>> 1995...
>>
>> Oliver
>
> Wohl kaum: const existiert in C++ seit "C with Classes" (lt. Wikipedia
> seit 1981) und MFC ist lt. Wikipedia von 1992.

Allerdings wurde C++ erst 1998 genormt. Davor gab es nicht wirklich DAS 
Standard-Regelwerk, an das sich alle (oder zumindest viele) zu halten 
versuchen. Also hat man sich bei Microsoft vermutlich um 
const-Korrektheit noch nicht geschert - vermutlich auch aus dem 
gewohnten laxen Umgang damit in C heraus.

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.