C++ Template spezialisation einer member function

OP #4315984
Lesenswert?

Moin zusammen,

Normalerweise kann man ja eine Memberfunktion, die eine const referrence 
als parameter bekommt, problemlos mit einem non-const object aufrufen, 
da der cast non-const -> const immer möglich ist:
1
class ClassA
2
{
3
    void f(const ClassB& b);
4
}
5
...
6

7
ClassA a;
8
ClassB b;
9
a.f(b);  //b ist nicht const, macht aber nichts

So weit so gut. Wenn ich aber jetzt ein template aus f mache gibts ein 
Problem:
1
class ClassA
2
{
3
    template<typename T>
4
    void f(T& t) {...};
5
    template<>
6
    void f(const ClassB& b) {...};
7
}
8
...
9

10
ClassA a;
11
ClassB b;
12
a.f(b);  //b ist nicht const, und deswegen funktioniert die spezialisierung nicht :-/

Jetzt wird die Funktion für ClassB plötzlich nicht mehr aufgerufen, 
sondern die generische. So weit ich weiß wird doch eigentlich immer die 
am weitesten spezialisiere Funktion, die auf den übergebenen Typ passt, 
aufgerufen. Und passen tut es, denn ohne template gehts ja auch.

Mach ich irgendetwas falsch? Oder muss ich jetzt echt für "ClassB&" und 
"const ClassB&" seperate spezialisierungen machen? Oder gibts nen 
besseren Weg?
#4316029
Lesenswert?

Ja so ist das nunmal mit den C++ Overload Regeln. Kein Cast ist besser 
als ein const hinzufügen also kriegst du die Funktion mit dem Template. 
Eine einfache Möglichkeit das zu umgehen ist bei der Template Variante 
auch ein const hinzuzufügen (falls der Code der Funktion das 
ermöglicht). Dann erfordern beiden Funktionen den gleichen Cast aber die 
Variante ohne Template "gewinnt".
#4316161
Lesenswert?

Da D. schrieb:


> aufgerufen. Und passen tut es, denn ohne template gehts ja auch.

Auch ohne Template würde bei
1
class ClassA
2
{
3
     void f(ClassB& b);
4
     void f(const ClassB& b);
5
}

die non-const Version bevorzugt werden.

Man kann sich das auch so merken: die const-Version wird nur dann 
benutzt, wenn es unbedingt sein muss, weil das Argument des Aufrufers 
selber const ist.

ein 'const' in einer Argumentschnittstelle ist so zu lesen: Die Funktion 
macht die Zusicherung, dass es das übergebene Argument nicht verändern 
wird.

Logischerweise passt ein derartiges const natürlich auch dann, wenn der 
Aufrufer ein Objekt hat, dass veränderbar wäre. Macht ja nix, wenn die 
Funktion dieses Objekt nicht verändern will. Sie könnte - aber wenn sie 
es nicht tut, ist es auch gut.
Aber umgekehrt geht das natürlich nicht. Wenn der Aufrufer ein Objekt 
hat, dass nicht verändert werden darf, dann muss die Funktion auch die 
entsprechende Zusicherung machen.

>
> Mach ich irgendetwas falsch? Oder muss ich jetzt echt für "ClassB&" und
> "const ClassB&" seperate spezialisierungen machen? Oder gibts nen
> besseren Weg?

Kommt auf die Funktion an. Wenn f das Objekt sowieso nicht verändern 
will, dann kann man natürlich in der nicht-const Version das const 
dazucasten und die const-Version aufrufen. Macht man das alles inline, 
dann kostet das auch keine Laufzeit.
OP #4316413
Lesenswert?

Sebastian V. schrieb:
> Eine einfache Möglichkeit das zu umgehen ist bei der Template Variante
> auch ein const hinzuzufügen

Gute Idee. Aber leider muss die template Variante das Objekt verändern 
können. Bzw. ich kann nur für die Spezialisierung zusichern, dass nichts 
verändert wird.

Karl H. schrieb:
> Auch ohne Template würde bei class ClassA
> die non-const Version bevorzugt werden.

Hm, ja, das erklärt es wohl. Der cast auf const wird nur gemacht, wenn 
keine direkt passende Funktion gefunden wird. Und ein template, bei dem 
alles erlaubt ist, passt halt für alles...

Karl H. schrieb:
> Wenn f das Objekt sowieso nicht verändern
> will, dann kann man natürlich in der nicht-const Version das const
> dazucasten und die const-Version aufrufen.

Ja, so mach ich es dann wohl. In der spezialisierten Variante muss ich 
das Objekt nicht verändern. Also rufe ich, um Codeduplikation zu 
vermeiden,in der nicht-const Version die const Version auf.

Danke euch!
#4316833
Lesenswert?

Vielleicht etwas übertrieben aber man kann auch mit SFINAE was zaubern 
um die Template Version für ClassB zu deaktivieren:
1
class ClassA
2
{
3
    template<typename T, typename = std::enable_if_t<!std::is_same<T, ClassB>::value>>
4
    void f(T& t) {}
5
    
6
    void f(const ClassB& b) {}
7
};
Wenn noch mehr Spezialisierungen dazu kommen ist das aber auch nicht 
gerade so schön.

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