2013-11-09 10 views
6

Ich versuche zu verstehen, wie std::tolower verwenden ...std :: tolower und Visual Studio 2013

#include <iostream> 
#include <string> 
#include <algorithm> 
#include <locale> 

int main() 
{ 
    std::string test = "Hello World"; 
    std::locale loc; 
    for (auto &c : test) 
    { 
     c = std::tolower(c, loc); 
    } 

    std::transform(test.begin(), test.end(), test.begin(), ::tolower); // 1) OK 
    std::transform(test.begin(), test.end(), test.begin(), std::tolower); // 2) Cryptic compile error 
    std::transform(test.begin(), test.end(), test.begin(), static_cast<int(*)(int)>(std::tolower)); // 3) Cryptic compile error. Seems OK with other compilers though 

    return 0; 
} 

So:

  1. Warum ::tolower Version funktioniert?
  2. Warum std::tolower funktioniert nicht in Std :: transform?
  3. Was versucht static_cast<int(*)(int)>(std::tolower)) wirklich zu tun? Warum funktioniert mit GCC und nicht mit Visual Studio 2013? Wie kann ich std::lower in Std :: Transform mit Visual Studio 2013 verwenden?
+3

Haben Sie versucht, einschließlich der '' Header, [wo ist 'std :: tolower' tatsächlich definiert ist] (http://en.cppreference.com/w/cpp/string/byte/tolower)? –

+0

oups, Oo '^^ Aber warum funktioniert es dann mit GCC? – Korchkidu

+0

@Korchkidu, denn mit GCC gehört zu einem der anderen Header, die Sie eingeschlossen haben, ''. Verwechseln Sie nie "es passiert, dass es mit einem Compiler funktioniert", was bedeutet, dass der Code tatsächlich korrekt ist, insbesondere in Bezug auf Header, die intern auch andere Header der Library enthalten. –

Antwort

8

Zunächst, beachten Sie, dass keine dieser Ansätze das Richtige auf eine tragbare Weise tut! Das Problem ist, dass char signiert werden kann (und in der Regel ist), aber die Versionen akzeptieren nur positive Werte! Das heißt, Sie wirklich std::tolower() mit so etwas wie diese verwenden möchten:

std::transform(test.begin(), test.end(), test.begin(), 
       [](unsigned char c) { return std::tolower(c); }); 

(oder, natürlich, mit einem entsprechenden Funktionsobjekt, wenn Sie mit C++ 03 stecken). Die Verwendung von std::tolower() (oder ::tolower() für diese Angelegenheit) mit einem negativen Wert führt zu undefiniertem Verhalten. Natürlich ist dies nur auf der Plattform wichtig, wo char signiert ist, was jedoch die typische Wahl zu sein scheint.

Ihre Fragen zu beantworten:

  1. Wenn <cctype> einschließlich Sie in der Regel die verschiedenen Funktionen und Typen aus der Standard-C-Bibliothek erhalten sowohl im Namensraum std sowie im globalen Namespace. Die Verwendung von ::tolower funktioniert normalerweise, funktioniert jedoch nicht garantiert.
  2. Wenn <locale> enthalten ist, sind zwei Versionen von std::tolower verfügbar, eine als int(*)(int) und eine als char(*)(char, std::locale const&). Bei Verwendung nur std::tolower hat der Compiler im Allgemeinen keine Möglichkeit zu entscheiden, welche zu verwenden ist.
  3. Seit std::tolower ist mehrdeutig, mit static_cast<int(*)(int)>(std::tolower) disambiguates welche Version zu verwenden. Warum die Verwendung von static_cast<...>() mit VC++ fehlschlägt, weiß ich nicht.
  4. Sie sollten std::tolower() mit einer Folge von char s sowieso nicht verwenden, da dies zu undefiniertem Verhalten führt. Verwenden Sie ein Funktionsobjekt mit std::tolower intern auf einem unsigned char.

Es ist erwähnenswert, dass eher ein Funktionsobjekt mit als einen Funktionszeiger ist in der Regel viel schneller, weil es trivial ist, die Funktion Objekt Inline aber nicht so trivial, die Funktionszeiger auf Inline.Compiler werden immer besser mit der Verwendung von Funktionszeigern, wo die Funktion tatsächlich bekannt ist, aber zeitgenössische Compiler funktionieren nicht immer direkt mit Funktionszeigern, selbst wenn der gesamte Kontext vorhanden wäre.

+0

Ich weiß nicht, ob dies auch für VS2013 gilt, aber die Dokumente für VS2012 geben an, dass die in ctype.h deklarierte Toleranz nicht sicher ist, es sei denn isupper Gibt einen Wert ungleich Null zurück, der bei einem Wert für die Seite bedeuten würde, dass die Verwendung in einer Transformation einen expliziten Test auf isupper für jedes Element in der Sequenz erfordern würde. Ich weiß, dass ich schon eine Menge Code gesehen habe, der diese Einschränkung ignoriert, daher bin ich neugierig zu wissen, ob irgendjemand hier einen Einblick hat. – Neutrino

+0

@Neutrino: Dies ist, was der C-Standard (ISO/IEC 9899: 2011; frühere Version sagte das gleiche, soweit ich sagen kann) zu diesem Thema in 7.4.2.1 Absatz 3 (Returns) zu sagen: "Wenn das Argument ist ein Zeichen, für das 'isupper' wahr ist und es gibt ein oder mehrere entsprechende Zeichen, die durch das aktuelle Gebietsschema angegeben sind, für die 'islower' wahr ist, die' tolower' Funktion gibt eines der entsprechenden Zeichen zurück (immer das gleiche) eine für jedes gegebene Gebietsschema); andernfalls wird das Argument unverändert zurückgegeben. " Ich sehe keine Einschränkung für das Argument, außer dass es ein gültiger "unsigned char" -Wert ist. –

+0

So hält sich Visual Studio an den Standard oder fügt, wie in der Dokumentation vorgeschlagen, eine zusätzliche nicht standardkonforme Integritätsbedingung hinzu. Hier wird ganz klar gesagt, dass, wenn isupper für das Argument nicht wahr zurückkehrt, die Ergebnisse "unerwartet" sein können, was auch immer das bedeutet. http://msdn.microsoft.com/en-us/library/8h19t214.aspx – Neutrino

6

std::tolower in C++ überlastet ist, wird es in <cctype> erklärt als

int tolower(int); 

und auch in <locale> als

template<CharT> CharT tolower(CharT, const locale&); 

so, wenn Sie sagen "std::tolower" Sie einen mehrdeutigen Verweis auf eine bekommen überladene Funktion.

  1. Warum ::tolower Version funktioniert?

Wenn Sie <cctype> die einargumentigen Überlastung enthalten ist std in Namensbereich deklariert und könnte auch im globalen Namensraum deklariert werden, auf dem Compiler abhängig. Wenn Sie <ctype.h> einschließen, ist es garantiert, dass es im globalen Namespace enthalten ist, und ::tolower wird funktionieren (obwohl Dietmars Punkte darüber, wann es nicht sicher ist) beachten. Die Zwei-Argument-Überladung von <locale> wird nie im globalen Namespace deklariert, so dass sich ::tolower niemals auf die Zwei-Argument-Überladung bezieht.

2. Warum funktioniert std::tolower nicht in std :: transform?

Siehe oben, es ist ein überladener Name.

3. Was versucht static_cast<int(*)(int)>(std::tolower)) wirklich zu tun?

Es teilt dem Compiler Sie die int std::tolower(int) Überlastung wollen, keine andere Überlastung von std::tolower.

Warum funktioniert es mit GCC und nicht mit Visual Studio 2013?

Wahrscheinlich, weil Sie umfaßte nicht <cctype>, oder (weniger wahrscheinlich) einen Visual Studio Fehler sein könnte.

4.Wie könnte ich std::lower in std::transform mit Visual Studio 2013 dann verwenden?

Wenn Sie wissen, dass Sie nur Zeichen mit Werten zwischen 0 und 127 haben, dann können Sie <ctype.h> umfassen und ::tolower verwenden (da die zweiargumentigen Version im globalen Namespace nicht deklariert wird, nur im Namensraum std) oder disambiguate die überladen Sie mit dem statischen Cast. Eine Alternative zu der Umwandlung ist eine lokale Variable zu verwenden:

typedef int (*tolower_type)(int); 
tolower_type tl = &std::tolower; 
std::transform(b, e, b, tl); 

eine sicherere und tragbare Alternative ist, ein Objekt benutzerdefinierte Funktion zu verwenden (oder Lambda-Ausdruck) sicher die gewünschte Überlastung zu nennen:

std::transform(b, e, b, [](unsigned char i) { return std::tolower(i); }); 

Dies verwendet std::tolower mit einem Argument, sodass der Compiler eine Überladungsauflösung ausführen kann, um zu ermitteln, welche Überladung Sie aufrufen möchten. Der Parameter ist unsigned char, um sicherzustellen, dass wir nie einen char mit einem negativen Wert an tolower(int) übergeben, weil das undefiniertes Verhalten hat.

Weitere Informationen finden Sie unter http://gcc.gnu.org/onlinedocs/libstdc++/manual/strings.html#strings.string.simple.

+1

Sie sollten besser wissen, als Leute zu raten, die '' Funktionen mit 'Char's zu verwenden! –

+0

yeah ... Ich füge eine Notiz hinzu, die auf Ihre bessere Antwort zeigt –