class rvalue used as lvalue

Gast #5384237
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));
#5384283
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 ...
(Firma: Torrox.de) #5384307
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++.
Gast #5384324
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);
Gast #5384382
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.
#5384456
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?
Gast #5384755
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));
#5384783
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 ...
Gast #5384938
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));
#5384968
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.
#5385041
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.
Gast #5385109
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.
(Firma: Torrox.de) #5385115
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.
(Firma: Torrox.de) #5385208
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.
(Firma: Torrox.de) #5385218
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.
#5385320
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 ...
#5385615
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
#5392228
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren