Ich probiere über ein Template einen immer gleich bleibenden Code für
Property-Durchgriffe zu reduzieren, das Template macht im echten Code
viel mehr, was aber für die Frage nicht relevant ist
das Mutable-Interface bekomme ich so von außen und darf es nicht ändern
in den Set/Get-Routinen passiert im Original-Code viel mehr - ist aber
für die Frage hier auch nicht relevant - nur kann ich den Aufbau der
Schnittstelle eben nicht ändern
zu folgendem Beispiel habe ich 2 Fragen:
1. warum kompiliert Test::setABC nicht
2. gibt es eine Möglichkeit bei dem Test::SetGet Template
Aufruf die "redundante" Nennung der beiden Zugriffs-Methode auf eine zu
reduzieren?
bis einschließlich C++17 ist alles erlaubt
gcc.godbolt.org: https://gcc.godbolt.org/z/65x6Ef
1
#include<string>
2
3
classMutable// das Interface bekomme ich so und kann es nicht aendern
4
{
5
public:
6
//get ist immer: ValueType NAME() const
7
//set ist immer: NAME(ValueType value_)
8
//Primitive werden als einfaches Value returned der Rest als const&
clang sagt doch eigentlich immer, was ihn stört.
Im dem Fall:
candidate template ignored: deduced conflicting types for parameter
'ValueType' ('const std::basic_string<char> &' vs.
'std::basic_string<char>')
Oliver
cppbert3 schrieb:> SetGet( &Mutable::ABC, &Mutable::ABC, value_ );
Kann der Compiler hier zwischen den overloads unterscheiden? Das ist das
erste was mir eingefallen ist und ich hab noch nicht weiter drüber
nachgedacht.
Oliver S. schrieb:> clang sagt doch eigentlich immer, was ihn stört.> Im dem Fall:> candidate template ignored: deduced conflicting types for parameter> 'ValueType' ('const std::basic_string<char> &' vs.> 'std::basic_string<char>')
Hatte vergessen den Fehler zu posten, ich sehe leider nicht wo ich die
const& Dekoration verliere?
mh schrieb:> cppbert3 schrieb:> SetGet( &Mutable::ABC, &Mutable::ABC, value_ );>> Kann der Compiler hier zwischen den overloads unterscheiden? Das ist das> erste was mir eingefallen ist und ich hab noch nicht weiter drüber> nachgedacht.
Ja über die Signatur die bei den beiden SetGet Parametern angegeben ist,
hat aber nichts mit dem Fehler zu tun
Oliver S. schrieb:> Wenn man dem Compiler mitSetGet<const std::string& >(>> auf die Sprünge hilft, klappt es.> Oliver
Ich würde lieber verstehen warum const& verloren geht anstatt dem
Kompiler unter die Arme zu greifen
Oliver S. schrieb:> candidate template ignored: deduced conflicting types for parameter> 'ValueType' ('const std::basic_string<char> &' vs.> 'std::basic_string<char>')
Da in
"ValueType value_" ein pass-by-value Parameter ist, geht da die Referenz
(und damit auch das const) verloren.
Perfect forwarding löst das Problem, dann hakt es aber bei der
int-Version.
Oliver
udok schrieb:> Ich habe ja nichts gegen C++, aber wenn ich das C++ Chinesisch> hier> lese,> dann freue ich mich wieder richtig auf C.
soo dolles chinesisch ist das gar nicht ich will nur was nicht ganz so
typisches machen und lerne immer mal wieder gerne was neues, von Hand
und mit Macros kann ich sowas ja schon seit Uhrzeiten :)
Oliver S. schrieb:> Mit> ..., typename std::remove_const<ValueType>::type value_ )> funktioniert es.>> Oliver
aber dann ist mein value_ ungewollt non-const - auch blöd, die
Folge-Untertemplates (die hier im Beispiel nicht drin sind) erwartet die
"Unversehrtheit" des Typs
natürlich ist klar das ich auch den Tip von Oliver so nutzen kann
>>Oliver S. schrieb:>> Wenn man dem Compiler mitSetGet<const std::string& >(>> auf die Sprünge hilft, klappt es.
aber das decltype+type ist dann schon ein wenig haesslich
1
classMutable// das Interface bekomme ich so und kann es nicht aendern
cppbert3 schrieb:> aber dann ist mein value_ ungewollt non-const - auch blöd, die> Folge-Untertemplates (die hier im Beispiel nicht drin sind) erwartet die> "Unversehrtheit" des Typscppbert3 schrieb:> //Primitive werden als einfaches Value returned der Rest als const&
Da const& eine Referenz auf ein const ist, ändert std::remove_const gar
nichts am Typ. Das Rätsel ist für ich nur noch, warum es damit
funktioniert.
Oliver
P.S. du kennst cppinsights?
Oliver S. schrieb:> cppbert3 schrieb:>> aber dann ist mein value_ ungewollt non-const - auch blöd, die>> Folge-Untertemplates (die hier im Beispiel nicht drin sind) erwartet die>> "Unversehrtheit" des Typs>> cppbert3 schrieb:>> //Primitive werden als einfaches Value returned der Rest als const&>> Da const& eine Referenz auf ein const ist, ändert std::remove_const gar> nichts am Typ. Das Rätsel ist für ich nur noch, warum es damit> funktioniert.>> Oliver> P.S. du kennst cppinsights?
ja - wunderbares Tool um die Magie von Lambdas zu erklären :)
cppbert3 schrieb:> wo bleibt Niklas wenn man ihn braucht - hier bricht schon> Gruppen-Depression aus :)
Hihi, mir ist auch keine besonders gute Lösung eingefallen. Die
vermutlich allgemeinste Lösung wäre:
Hat den kleinen Vorteil dass die Member-Pointer nicht zur Laufzeit
verarbeitet werden müssen (effizienter). Das explizite Casten der
Pointer via static_cast ist aber ziemlich lästig, aber mehr oder weniger
nötig weil die Setter ja so inkonsistente Signaturen haben.
SetGet ist dafür ziemlich allgemein und macht kaum Annahmen über die
Signaturen von Setter/Getter; der spezifische Teil ist in setXYZ und
setABC gekapselt.
Es braucht noch nicht mal std::remove_const, ein einfacher "Zwischentyp"
reicht aus. Erklären kann ich zwar immer noch nicht, wieso das den
Compiler dazu bringt, die richtige type deduction zu finden, aber es
funktioniert.
https://gcc.godbolt.org/z/Exj4h8
Oliver
Oliver S. schrieb:> Es braucht noch nicht mal std::remove_const, ein einfacher> "Zwischentyp"> reicht aus. Erklären kann ich zwar immer noch nicht, wieso das den> Compiler dazu bringt, die richtige type deduction zu finden, aber es> funktioniert.>> https://gcc.godbolt.org/z/Exj4h8>> Oliver
1
template<typenameT>
2
structTT
3
{
4
usingtype=T;
5
};
ja das ist wirklich seltsam, wie bist du auf den Test gekommen?
vielleicht wir dadurch irgendwie forciert das er den Type erst aus dem
Setter oder Getter deducted und nicht dieser &-Verlust durch den
value-Parameter passiert?
Vincent H. schrieb:> Sofern alle getter/setter Entweder value oder const-ref basiert> sind> könnte man den Compiler auch einfach fragen:> template <typename ValueType,> typename RefOrValue = std::conditional_t<> std::is_reference_v<ValueType>, const ValueType&,> ValueType> >> void SetGet(Getter<Mutable, RefOrValue> getter_,> Setter<Mutable, RefOrValue> setter_, ValueType value_) {}
ja das könnte so klappen - die komischerweise funktionierende Kurz-Form
von Oliver wäre mir dann aber schon lieber
kann das jemand erklären?
Oliver S. schrieb:> cppbert3 schrieb:>> ja das ist wirklich seltsam, wie bist du auf den Test gekommen?>> Weil std::remove_const da ja auch nichts anders macht.>> Oliver
1
template<typenameT>
2
usingTT=T;
geht nicht - und da sehen ich wirklich keinen Unterschied mehr
cppbert3 schrieb:> Oliver S. schrieb:>> cppbert3 schrieb:>>> ja das ist wirklich seltsam, wie bist du auf den Test gekommen?>>>> Weil std::remove_const da ja auch nichts anders macht.>>>> Oliver> template <typename T>> using TT = T;>> geht nicht - und da sehen ich wirklich keinen Unterschied mehr
d.h. durch den struct TT Scope wird da irgendwas anders deducted
btw passiert mir sowas ständig - ich denke etwas müsste so funktionieren
und dann stolpere ich durch die Untiefen der
Templare-Parameter-Deduction und dann schließen noch andere mit an :)
cppbert3 schrieb:> vielleicht wir dadurch irgendwie forciert das er den Type erst aus dem> Setter oder Getter deducted und nicht dieser &-Verlust durch den> value-Parameter passiert?
Den Typ deducted der ja auch in der Originalversion aus allen drei
Parametern, nur kommt er da ohne den Zwischentyp auf const std::string&
für die Funktionspointer, während er für value_ den einfachsten Weg geht
und einen std::string deducted. Das führt dann zur o.a. Fehlermeldung.
Oliver
Niklas G. schrieb:> Hat den kleinen Vorteil dass die Member-Pointer nicht zur Laufzeit> verarbeitet werden müssen (effizienter). Das explizite Casten der> Pointer via static_cast ist aber ziemlich lästig, aber mehr oder weniger> nötig weil die Setter ja so inkonsistente Signaturen haben.
d.h. du gehst davon aus das Olivers Zwischen-Struct Lösung mehr
calls/code erzeugt? (ist bei gcc.godbolt ein wenig schwer zu erkennen)
Ich hab jetzt die Erklärung, warum das mit dem Zwischentyp funktioniert.
Damit wird er Typ von value_ nicht detuctible.
https://stackoverflow.com/a/12566270
Oliver
cppbert3 schrieb:> d.h. du gehst davon aus das Olivers Zwischen-Struct Lösung mehr> calls/code erzeugt?
Ja, wenn SetGet nicht inlined wird. "getter" und "setter" sind ja
Funktions-Parameter, welche also auch übergeben werden müssen. Das kann
man mit "__attribute__ ((noinline))" ausprobieren.
Oliver S. schrieb:> Der Zwischentyp erzeugt keine Code, der sorgt nur dafür, daß der> Compiler den Typ von value_ richtig (=compilierbar) erkennt.>> Oliver
das ist mir klar - ich denke ohne Struct oder mit using ist es nur ein
alias - in dem Struct ist es ein Struct-lokaler Type der anders gewertet
wird
Niklas G. schrieb:> SetGet <static_cast<const std::string& (Mutable::*) () const>> (&Mutable::ABC),> static_cast<void (Mutable::*) (const std::string&)>> (&Mutable::ABC)> (value_ );
du hast recht dein Code wird erzeugt den selben Code als hätte man es
von Hand ausgeschrieben, Olivers Stand (der schöner aussieht) macht
einen call für setXYZ(int)
also konzentriere ich mich mal auf deinen Stand - weil ich möchte
definitiv nicht ein Template einführen und damit dann auch noch die
inline-Fähigkeit stören
aber trotzdem Danke Oliver! für deine Lösungen
cppbert3 schrieb:> Niklas G. schrieb:>> SetGet <static_cast<const std::string& (Mutable::*) () const>>> (&Mutable::ABC),>> static_cast<void (Mutable::*) (const std::string&)>>> (&Mutable::ABC)> (value_ );>> du hast recht dein Code wird erzeugt den selben Code als hätte man es> von Hand ausgeschrieben, Olivers Stand (der schöner aussieht) macht> einen call für setXYZ(int)>> also konzentriere ich mich mal auf deinen Stand - weil ich möchte> definitiv nicht ein Template einführen und damit dann auch noch die> inline-Fähigkeit stören>> aber trotzdem Danke Oliver! für deine Lösungen
und auch danke an die anderen
jetzt muss ich nur noch das ge-caste ein wenig kleiner bekommen - ich
bau mal ein trait das mir fuer einen type die Setter/Getter-types
liefert und schaue ob dabei mein inlining wieder zerstört wird
cppbert3 schrieb:> du hast recht dein Code wird erzeugt den selben Code als hätte man es> von Hand ausgeschrieben, Olivers Stand (der schöner aussieht) macht> einen call für setXYZ(int)
So, wie er Code da steht (also mit inlining) erzeugen beide Varianten
den gleichen Code. Bei solch kurzen Programmen behält der Optimierer da
schon den Überblick.
Trotzdem würde ich auch die Version von Niklas bevorzugen.
Funktionspointer sind ja sowas von C, wenn es aber nunmal nicht ohne
geht, dann wenigstens so C++"isch", wie möglich ;)
Oliver
Oliver S. schrieb:> cppbert3 schrieb:>> du hast recht dein Code wird erzeugt den selben Code als hätte man es>> von Hand ausgeschrieben, Olivers Stand (der schöner aussieht) macht>> einen call für setXYZ(int)>> So, wie er Code da steht (also mit inlining) erzeugen beide Varianten> den gleichen Code. Bei solch kurzen Programmen behält der Optimierer da> schon den Überblick.>> Trotzdem würde ich auch die Version von Niklas bevorzugen.> Funktionspointer sind ja sowas von C, wenn es aber nunmal nicht ohne> geht, dann wenigstens so C++"isch", wie möglich ;)>> Oliver
nein ich habs getestet der clang und der gcc inlinen nicht - weil man
Funktione-Pointer Parameter nicht optimieren darf, wenn die als Template
value type Parameter komme aber schon
das hier ist jetzt meine entgültige Version des Prototyps
- volles inlineing und das hilfs-template kann von aussen kommen :)
gcc.godbolt.org: https://gcc.godbolt.org/
relativ kurz und schnuckelig
1
#include<string>
2
3
classMutable// das Interface bekomme ich so und kann es nicht aendern
4
{
5
public:
6
//get ist immer: ValueType NAME() const
7
//set ist immer: NAME(ValueType value_)
8
//Primitive werden als einfaches Value returned der Rest als const&
cppbert3 schrieb:> nein ich habs getestet der clang und der gcc inlinen nicht - weil man> Funktione-Pointer Parameter nicht optimieren darf, wenn die als Template> value type Parameter komme aber schon
Da in beiden Versionen sämtliche Parameter zur Compilezeit bekannt sind,
optimiert der Compiler das in beiden Fällen soweit zusammen, wie es
geht.
https://gcc.godbolt.org/z/61Ke6K
Oliver
Oliver S. schrieb:> cppbert3 schrieb:>> nein ich habs getestet der clang und der gcc inlinen nicht - weil man>> Funktione-Pointer Parameter nicht optimieren darf, wenn die als Template>> value type Parameter komme aber schon>> Da in beiden Versionen sämtliche Parameter zur Compilezeit bekannt sind,> optimiert der Compiler das in beiden Fällen soweit zusammen, wie es> geht.>> https://gcc.godbolt.org/z/61Ke6K>> Oliver
hatte ich auch gedacht und jetzt auch durch dich bestätigt, es gab bei
mir zeitweise calls auf setXYZ im Code (mit -O2) das ist mit deiner
Variante passiert und mit Niklas Variante nicht - ich hätte auch
erwartet das beides inlined
dein gcc.godbolt Link von vom 09.12.2020 11:01 inlined nicht wenn er mit
dem gcc gebaut wird - clang macht inlining, wenn es kompiletime
parameter sind machen es beide
https://gcc.godbolt.org/z/bMa6rc