2017-02-23 2 views
6

Ich denke derzeit darüber nach, wie ein generischer Typ einer Vorlage zu einem std::sting sowie String-Literale am besten beschränken. Daher vergleiche ich den abgeleiteten Typ mit dem gewünschten Typ unter Verwendung von . Im Falle eines std::string funktioniert das sofort. Bei einem String-Literal, dh einem char const-Array, funktioniert es nur, wenn ich std::decay für den Typ verwende und dann das Ergebnis mit dem Typ char const * vergleiche. Wenn ich den abgeleiteten Typ direkt mit dem vergleichen würde, was er meiner Meinung nach sein sollte, gibt is_same den Wert false zurück, wie der folgende Beispielcode zeigt.Vorlage Argument Abzug von String Literal

template <class TYPE> 
void function(TYPE&& parameter) 
{ 
    //this doesn't work as expected 
    std::cout << typeid(TYPE).name() << " : " << typeid(char const [5]).name() << std::endl; 
    std::cout << std::is_same<char const [5], TYPE>::value << std::endl; 
    //this works as expected 
    std::cout << typeid(std::decay_t<TYPE>).name() << " : " << typeid(char const *).name() << std::endl; 
    std::cout << std::is_same<char const *, std::decay_t<TYPE>>::value << std::endl; 
} 

int main(int argc, char** argv) 
{ 
    function("name"); 
    return 0; 
} 

Der Ausgang erzeugt wird, ist die folgende:

char const [5] : char const [5] 
0 
char const * __ptr64 : char const * __ptr64 
1 

Nun, was ich frage mich, warum ist is_same false zurück im ersten Fall, obwohl die Typen identisch zu sein scheinen.

die einzig mögliche Erklärung, die ich mit kommen könnte, ist, dass in der Funktion std::is_same eine Transformation ähnlich wie std::decay auf den Typen angewandt wird (beispielsweise ein Funktionsaufruf). Aber dann würde diese Transformation auch für den anderen Typ eintreten, was das gleiche Ergebnis und damit Gleichheit ergeben würde.

+0

Warten Sie bis C++ 17 und schreiben Sie Ihre Vorlage, um 'std :: string_view' zu verwenden. –

+1

Versuchen Sie, Referenzen von 'TYPE' zu entfernen. String-Literale sind L-Werte. –

+0

Danke, du hattest Recht. Das wusste ich nicht. Ändern der Zeile zu 'std :: ist_same > :: value' ergibt Gleichheit. Warum werden String-Literale als L-Werte betrachtet? Wenn es ein int literales wäre, würde es stattdessen als rvalue betrachtet, oder? Und können wir das vielleicht zur akzeptierten Antwort machen? – user1488118

Antwort

6

Stringliterale werden nicht von Wert als char const [N], sondern durch Referenz als char const (&)[N] geben.

Das funktioniert richtig für mich:

std::cout << std::is_same<char const (&)[5], TYPE>::value << std::endl; 

Hinweis here dass

1) Bezieht sich auf ein std::type_info Objekt repräsentiert den Typ Typ. Wenn Typ ein Referenztyp ist, bezieht sich das Ergebnis auf ein std::type_info Objekt, das den referenzierten Typ darstellt.

können Sie leicht überprüfen, ob is_same nicht Referenz-ness in der gleichen Weise wie type_info, beispielsweise durch die Überprüfung, dass

std::is_same<int&, int>::value == false 

Dies erklärt, warum die typeid Name der gleiche ist, aber Ihr verwirft is_same Test schlägt immer noch fehl.

2

gcc benutzerdefinierte Funktion verwenden:

template < class T > 
constexpr std::string type_name() 
{ 
    std::string p = __PRETTY_FUNCTION__; 
    return p.substr(43 + 10, p.length() - 100 - 1 - 10); 
} 

und Hinzufügen zu Ihrem Code:

std::cout << type_name<TYPE>() << " : " << type_name<char const [5]>() << std::endl; 

Die Ergebnisse sind:

A5_c : A5_c 
0 
const char (&)[5] : const char [5] 

So müssen Sie std::remove_reference auf TYPE verwenden.

+0

Ich denke, du hast Recht bezüglich der Effekte. Die Ursache dafür wurde jedoch von Kerrek SB in den Kommentaren erwähnt: String-Literale sind offensichtlich L-Werte, keine R-Werte. Daher wird der Typ als & ldquor; T & "anstelle von & ldquor; T" abgeleitet, wenn er über einen weiterleitenden Referenzparameter weitergegeben wird. – user1488118

+0

@ user1488118 Wenn Sie dasselbe String-Literal in Ihrem Code verwenden, zum Beispiel '" string "' an verschiedenen Stellen, ist es tatsächlich die gleiche Zeichenfolge. Es ist nicht jedes Mal neu, aber es bleibt so lange im Speicher, wie das Programm läuft. Wenn Sie also darauf verweisen, beziehen Sie sich tatsächlich auf den Verweis auf den Wert l. – xinaiz

0

Es kann ein Fehler des Compilers sein. Der Compiler erzeugt solche types-Objekte, die von typeid bei compiletime angegeben werden. Der Compiler würde Array-Typen für jede Länge (0 bis 2 ** n) nicht kompilieren, so kompiliert er sie bei Bedarf, und es kann über das Duplikat "vergessen". Versuchen Sie, eine spezielle Vorlage zu verwenden, in der Sie die Länge vom enthaltenen Typ trennen. Dies ist nicht der Typ, aber Sie können prüfen, ob es gleich einem anderen ist.

template<class T, size_t size> 
struct array_t{}; 

    template <class T, size_t size> 
void function(T[size] parameter) { 
    std::cout << typeid(array_t<T, size>).name() << " : " << typeid(array_t<char, 5>).name() << std::endl; 
    std::cout << std::is_same<array_t<T, size>, array_t<char, 5>>::value << std::endl; 
}; 
Verwandte Themen