Ich denke, ich würde dies unter Verwendung der Vorlagen, die bereits in der Standardbibliothek verfügbar sind, angehen. Es erfordert keine Vererbung oder benutzerdefinierte Klassen.
#include <utility>
template<typename T, bool canChange>
struct Data{
using value_type = T;
using cv_type = std::conditional_t<canChange, value_type, std::add_const_t<value_type>>;
using reference = std::add_lvalue_reference_t<cv_type>;
using const_reference = std::add_lvalue_reference_t<std::add_const_t<cv_type>>;
Data(T t) : m_data(std::move(t)) {}
cv_type m_data; //< This makes it const/non-const at compile time.
// This function will also make the return type const/non-const
// at compile time.
reference GetDataRef(){ return m_data;}
//However, it seems to me that I still need a second function
//with an explicit "const", which I can't seem to avoid.
const_reference GetDataRef() const {return m_data;}
};
int main()
{
Data<int, true> d1 { 10 };
d1.m_data = 12;
const Data<int, true>& rd1 = d1;
auto& a = d1.GetDataRef();
auto& b = rd1.GetDataRef();
a = 12; // compiles fine
// b= 12; won't compile
Data<int, false> d2 { 10 };
const Data<int, false>& rd2 = d2;
auto& c = d2.GetDataRef();
auto& d = rd2.GetDataRef();
// c = 12; // won't compile
// d = 12; // won't compile
}
Nun zur Frage:
Kann ich irgendwie vermeiden, dass zwei const/nicht-const-Funktionen hier bei der Kompilierung einige SFINAE Magie mit?
Sie antworten fast Ihre eigene Frage hier. SFINAE erfordert, dass Vorlagenargumente im unmittelbaren Kontext betrachtet werden. Das ist eine komplexe Art zu sagen, dass der Ausdruck in std::enable_if<>
von einem bestimmten Schablonentyp abhängen muss.
Leider ist der Schablonentyp von T zu dem Zeitpunkt bekannt, zu dem die Funktion GetDataRef
ausgewertet wird, daher wird enable_if uns hier nicht weiterhelfen.
Wenn wir also nur eine Version von GetDataRef
wollen, müssten wir tatsächlich auf die Ableitung aus einem Schablonentyp zurückgreifen (die Basisklasse würde dann im unmittelbaren Kontext von T ausgewertet).
Allerdings gibt es auch dann ein Problem.
berücksichtigen:
Data<int, true>& x
Dies ist ein Hinweis auf veränderbare Behälter Dies ist veränderbare Daten enthalten
const Data<int, true>& y
ein Verweis auf einen unveränderlichen Behälter enthält veränderbare Daten
Aufruf x.GetDataRef()
sollte eine veränderliche Referenz auf ein int zurückgeben, sonst werden wir unsere Benutzer verwirren.
Aufruf y.GetDataRef()
sollte sicherlich einen const Verweis auf ein int zurückgeben, sonst wieder, Benutzer können schockiert sein zu erfahren, dass ein Mitglied einer const Sache tatsächlich veränderbar ist.
Warum nicht nur die 'const' Version behalten, wenn Sie nicht überladen müssen? – KABoissonneault
@KABoissonneault Ich brauche beide Versionen, da sich die Konstanz der Vorlage je nach Template-Parameter ändert. Nur um zu verdeutlichen, möchte ich in der Lage sein, bestimmte "Data" -Strukturen zu erstellen, die zu einem späteren Zeitpunkt geändert werden können, während sich einige sicher nicht ändern werden. Daher die Notwendigkeit für beide const/non-const-Funktionalität. – cplusplusrat