2010-12-07 19 views
2

Ich habe eine Klasse mit Templaten, die einen Wert enthält. Ist es möglich, der Klasse einen Konstruktor hinzuzufügen, um eine implizite Konvertierung zu ermöglichen, wie im folgenden Beispiel?Konstruktor zur Vorlagenklasse hinzufügen

Oder gibt es einen besseren Weg, dies zu tun?

#include <string> 

template<typename T> 
class Value 
{ 
    public: 
    Value(const T& value) : m_value(value) { }; 
    private: 
    T m_value; 
}; 

// I thought adding something like this would do the trick but it does not work: 
/* 
template<> 
class Value<std::string> 
{ 
    public: 
    Value(const char *sz) : m_value(sz) { }; 
} 
*/ 

void f(const Value<std::string> &s) 
{ 
} 

int main() 
{ 
    f(std::string("hello")); 

    // I want to have this working: 
    f("hello"); 
} 

Antwort

4

Aufruf f(const Value<std::string>&) mit einem Stringliteral würde zwei benutzerdefinierte Konvertierungen erfordern (const char[] ==>std::string ==>Value<std::string>), um die Funktionsparameter anzupassen, während der Standard nur erlaubt eins.
Ich sehe zwei Möglichkeiten, das zu lösen: Entweder den Konstruktor überlasten oder f() überlasten.

Angenommen, Sie fragen nach dem ersten, weil letzteres nicht möglich ist, gibt es mehrere Möglichkeiten, den Konstruktor zu überlasten.

Sie können die Tatsache ausnutzen, dass Memberfunktionen einer Klassenvorlage nur kompiliert werden, wenn sie aufgerufen werden, und einen Konstruktor hinzufügen, der nur kompiliert wird, wenn T einen bestimmten Typ hat. Wenn Benutzer Ihrer Vorlage diese für andere Typen aufrufen, führt dies natürlich zu einem Fehler. Anstatt jedoch ein Problem darin zu sehen, könnte man es Umarmung durch den Konstruktor ein Mitglied Vorlage machen:

template<typename U> 
Value(const U& value) : m_value(value) { }; 

auf diese Weise, was auch immer in T umgewandelt werden kann (wie auch T selbst, natürlich) ist erlaubt für U.

Oder Sie könnten die Klasse für std::string spezialisieren. Leider müssten Sie dann die ganze Klasse spezialisieren, da es keine selektive Spezialisierung einzelner Mitglieder gibt. In diesem Fall möchten Sie möglicherweise den gesamten Code in eine gemeinsame (möglicherweise private Basisklasse von Value, mit der Value Basisvorlage nur Konstruktoren, die an die Konstruktoren der Basisklasse weiterleiten, und eine Spezialisierung Value<std::string>, die einen weiteren Konstruktor unter fügt hinzufügen .

+0

ich dachte, es implizit konvertierbar war auch zu machen, aber der Compiler (g ++) beschwert sich darüber (ungültige Initialisierung der Referenz). – rve

+0

und nein, ist die Zeichenfolge kein Platzhalter – rve

+0

@rve: Tut mir leid, das ist ein Brainfart von mir war. Ich konnte nicht erkennen, dass Sie 'f()' mit einer C-Zeichenfolge aufrufen wollten. Ich werde meine Antwort reparieren. – sbi

1

Sie können der Klasse nicht hinzufügen, aber Sie können die gesamte Klasse spezialisieren.

1

Versuchen this:

template<typename T> 
class Value 
{ 
    public: 
    Value(const T& value) : m_value(value) { } 
    Value(const char* a): m_value(a){} // add this 
    private: 
    T m_value; 
}; 
+0

sicher es funktioniert, aber es macht nicht viel Sinn für Wert rve

+0

@rve machen: Meine einzige Absicht war, um Ihren Code frei von Kompilierungsfehlern ;-) –

2

Sie nicht können. Dies ist eine explizite Entscheidung der C++ Designer. der Grund dafür ist, dass der Compiler die Jagd nach möglichen Umwandlungen gehen müssten, und im allgemeinen würde dies eine unendliche Jagd sein.

Sure , Sie denken, dass const char[] ==>std::string ==>Value<std::string> logisch ist, aber der Compiler hat keine std::string. Es hat nur const char[] ==> ??? ==>Value<std::string>, und es müsste einen Typ in der Mitte finden. Zum Beispiel könnte es irgendwo einen class Foo geben, der einen Foo::Foo(const char*) Konstruktor und einen Foo::operator Value<std::string>() const hat. Das würde auch funktionieren. Wie Sie sehen können, gibt es weder const char[] noch Value<std::string>, die auf Foo zeigen. Daher müsste der Compiler auf eine Blindjagd gehen.

Als der Autor von Value, haben Sie jedoch eine Wahl.Sie können den Compiler darüber informieren, dass ein Value<std::string> kann von jeder Art konstruiert sein, das std :: string :: string akzeptiert:

template<typename T> 
class Value 
{ 
    public: 
    Value(const T& value) : m_value(value) { }; 
    // Intentionally not "explicit" 
    template<typename U> Value(const U& value) : m_value(value) { }; 
    private: 
    T m_value; 
}; 

Nun, wenn Sie void f(const Value<std::string> &s) als f("hello") nennen, gibt es eine einzige implizite Konvertierung Value<std::string>::Value<const char*>(const char* const&)

Verwandte Themen