Sollten wir bei der Verwendung von C-Headern in C ++ Funktionen von std :: oder dem globalen Namespace verwenden?

112

C ist etwas, nicht genau, eine Teilmenge von C ++. Daher können wir die meisten C-Funktionen / Header in C ++ verwenden, indem wir den Namen ein wenig ändern ( stdio.hto cstdio, stdlib.hto cstdlib).

Meine Frage ist eigentlich eine Art Semantik. In C ++ Code (unter Verwendung der neuesten Version von GCC - Compiler), kann ich anrufen printf("Hello world!");und std::printf("Hello world!");und es funktioniert genau gleich. Und in der Referenz, die ich benutze, erscheint es auch als std::printf("Hello world!");.

Meine Frage ist, ist es bevorzugt, std::printf();in C ++ zu verwenden? Ist da ein Unterschied?

DeiDei
quelle
17
Für den Fall, dass eines Tages das Ablegen von CBibliothekssymbolen in den globalen Namespace vorgeschrieben ist, bevorzuge ich die Verwendung der std::qualifizierten Versionen. (Außerdem wünschte ich mir, sie hätten es illegal gemacht).
Galik
3
@ Galik: Einverstanden. Das würde viele dumme Fragen zu C-Problemen mit einem C ++ - Compiler ersparen.
zu ehrlich für diese Seite
7
Es gibt kein "ein bisschen schwanger". Entweder ist C eine Teilmenge oder nicht. Tatsache ist, es ist nicht . Aus diesem Grund müssen die C-Header geändert werden, damit sie in C ++ funktionieren.
zu ehrlich für diese Seite
2
"fast alle" ist eine ziemlich nutzlose Maßnahme, wenn es um eine Reihe unzähliger Elemente geht. Mit dem gleichen Argument könnten Sie wahrscheinlich C und Java in Beziehung setzen.
Daniel Jour
9
@ Sasauke Nein, es ist keine Teilmenge. C und C ++ teilen sich definitiv eine Teilmenge, aber C selbst ist keine Teilmenge von C ++.
Das paramagnetische Croissant

Antworten:

105

Aus dem C ++ 11 Standard (Schwerpunkt Mine):

D.5 C Standardbibliotheks-Header [Depr.c.headers]

  1. Zur Kompatibilität mit der C-Standardbibliothek ...
  2. Jeder C-Header, von dem jeder einen Namen mit dem Formularnamen.h hat , verhält sich so, als ob jeder Name, der durch den entsprechenden cname- Header im Standardbibliotheks-Namespace platziert wird, innerhalb des globalen Namespace- Bereichs platziert wird. Es ist nicht spezifiziert , ob diese Namen werden zuerst im Namespace Gültigkeitsbereich deklariert oder definiert (3.3.6) aus dem Namensraum std und werden dann in den globalen Namespace Umfang durch explizite Verwendung Klärungen injiziert (7.3.3).
  3. Beispiel: Der Header stellt seine Deklarationen und Definitionen <cstdlib> sicher im Namespace std bereit . Diese Namen können auch im globalen Namespace angegeben werden. Der Header enthält im globalen Namespace mit <stdlib.h> Sicherheit dieselben Deklarationen und Definitionen wie im C-Standard. Diese Namen können auch im Namespace angegeben werden .std

Wenn die Überschriften «name.h» nicht mehr verwendet werden, wurden sie als Kandidaten für die Entfernung aus zukünftigen Revisionen identifiziert.

Daher würde ich vorschlagen, die Header «cname» einzuschließen und die Deklarationen und Definitionen aus dem stdNamespace zu verwenden.

Wenn Sie aus bestimmten Gründen die Header «name.h» verwenden müssen (veraltet, siehe oben), würde ich empfehlen, die Deklarationen und Definitionen aus dem globalen Namespace zu verwenden.

Mit anderen Worten: bevorzugen

#include <cstdio>

int main() {
    std::printf("Hello world\n");
}

Über

#include <stdio.h>

int main() {
    printf("Hello world\n");
}
sergej
quelle
1
N3242 ist kein C ++ - Standard. N3337 der Entwurf mit den geringsten Unterschieden zu C ++ 11.
MM
3
Siehe auch Jonathan Wakelys Warum <cstdlib> ist komplizierter als Sie vielleicht aus den Red Hat-Blogs denken . Er beschreibt eine Reihe von Problemen aus der Sicht eines C ++ - Standardbibliotheksimplementierers. Er bietet auch eine Historie, die auf C ++ 98 zurückgeht.
JWW
@sergej - Würdest du zufällig die C ++ 03-Behandlung zu diesem Thema kennen? Oder ist es ein Hit oder Miss, was passieren wird?
JWW
5
<name.h> ist möglicherweise veraltet. Es besteht keine Chance, dass sie bald entfernt werden. Im Gegenteil. Es gibt einen Vorschlag zum Entfernen des veralteten Etiketts, siehe open-std.org/JTC1/SC22/WG21/docs/papers/2017/p0619r0.html#3.5 . "Schließlich scheint es klar zu sein, dass die C-Header als wichtige Kompatibilitätsschicht mit C und POSIX im Wesentlichen für immer erhalten bleiben. Es kann sich lohnen, die Header nicht zu unterschätzen, [..]"
Sjoerd
81

<cmeow>liefert immer ::std::purrund kann oder kann nicht liefern ::purr.

<meow.h>liefert immer ::purrund kann oder kann nicht liefern ::std::purr.

Verwenden Sie das Formular, das garantiert von der von Ihnen angegebenen Kopfzeile bereitgestellt wird.

TC
quelle
7
STL in schlechter Verkleidung?
nwp
@nwp nein. (15 Zeichen)
TC
@TC Leider, wie ich versuchte , auf meinem Compiler, weder <cmeow>noch <meow.h>stellt weder ::std::purrnoch , ::purrsondern ein Pre-Prozessor Fehler. Nur <cstdio>und / oder <stdio.h>bietet ::std::printfund / oder ::printf. : P
LF
4
@LF Möglicherweise müssen Sie strcatproduzieren ::purr.
Lundin
8

Nein, dir geht es so oder so gut.

Die ursprüngliche Absicht war, dass die <___.h>Header die C-Versionen sind, die alles in den globalen Namespace <c___>einfügen , und die Header die C ++ - ified-Versionen sind, die alles in den stdNamespace einfügen .

In der Praxis fügen die C ++ - Versionen jedoch auch alles in den globalen Namespace ein. Und es gibt keinen klaren Konsens darüber, dass die Verwendung der std::Versionen "das Richtige" ist.

Verwenden Sie also grundsätzlich das, was Sie bevorzugen. Am häufigsten wird wahrscheinlich die C-Standardbibliotheksfunktion im globalen Namespace ( printfanstelle von std::printf) verwendet, aber es gibt nicht viel Grund, eine als "besser" als die andere zu betrachten.

jalf
quelle
2
"Und es gibt keinen klaren Konsens darüber, dass die Verwendung der std :: -Versionen" das Richtige "ist." Äh, ja, es besteht absolut Konsens darüber, dass dies das Richtige ist.
Miles Rout
4
Wie kann man objektiv feststellen, ob ein Konsens erzielt wurde oder nicht?
Jeremy Friesner
9
@JeremyFriesner Sie posten darüber auf SO und sehen, ob Sie nicht übereinstimmende Kommentare erhalten. :)
Jalf
1
@JeremyFriesner: Der Standard garantiert nicht, dass die C ++ - Headerversionen die Bezeichner in den globalen Namespace einfügen. Der Standard lehnt auch die C-Header-Versionen ab. Das sieht für mich ziemlich konsensorientiert aus. ;-)
DevSolar
2
@DevSolar dann das Wort "Konsens" in einem Wörterbuch nachschlagen. Es geht nicht darum, was der Standard sagt, sondern was C ++ - Programmierer sagen - und insbesondere, was sie tun . Es gibt einen Grund , warum buchstäblich jede Standardbibliotheksimplementierung die C-Header bereitstellt und die C ++ - Header auch alles in den globalen Namespace einfügen. :)
Jalf
3

Der einzige Unterschied besteht darin, dass Sie sich std::printf()durch Hinzufügen einer std::Bereichsauflösung in Zukunft vor jemandem schützen, der eine Funktion mit demselben Namen schreibt, was zu einem Namespace-Konflikt führen würde. Beide Verwendungen führen zu genau denselben OS-API-Aufrufen (Sie können dies unter Linux überprüfen, indem Sie es ausführen strace your_program).

Ich finde es sehr unwahrscheinlich, dass jemand eine solche Funktion benennt, da dies printf()eine der am häufigsten verwendeten Funktionen ist. In C ++ werden iostreams gegenüber Aufrufen von cstdioFunktionen wie printf bevorzugt .

Syntagma
quelle
1
Im Gegenteil, ich finde es ziemlich wahrscheinlich: printfIst in C ++ aufgrund des Mangels an starker Typisierung schmerzlich kaputt, ist es ganz natürlich, es durch eine bessere Version zu ersetzen.
Konrad Rudolph
1
@KonradRudolph Sie können es so finden, wenn Sie möchten, aber Sie würden sich irren; Es ist nicht für starkes Tippen gedacht, und es gibt viele Probleme, die mit dem erforderlichen starken Tippen nicht einfach gelöst werden können. Deshalb sind viele vergleichbare C ++ - Lösungen viel langsamer als printf. Wenn Sie es durch eine "bessere" Version ersetzen möchten, brechen Sie den Vertrag zwischen Sprache und Programmierer und befinden sich zunächst in einem Zustand der Sünde.
Alice
1
@ Alice Uhm, ich breche keinen Vertrag: std::printfunterscheidet sich von mynamespace::printfund C ++ erlaubt mir explizit, meine eigenen Funktionen zu definieren, deren Namen die von Funktionen im Inneren beschatten std. Das ist einfach nicht umstritten. Was Ihre Behauptungen printfbetrifft, die aufgrund der losen Eingabe effizient sind, ist das natürlich auch falsch. printfist nicht einmal besonders effizient, es gibt viel effizientere Implementierungen, die stark typisiert sind.
Konrad Rudolph
@KonradRudolph Absolut falsch; Sie brechen den im Standard geschriebenen Vertrag, dass printf ohne Quantifizierer eindeutig für ein C-Konstrukt gilt. Die Verwendung eines Namespace unter Aliasing des globalen Namespace ist keine gute Idee. Das ist einfach nicht umstritten .
Alice
5
@ Alice Kannst du bitte den Standard dazu zitieren? Mir ist keine solche Redewendung bekannt.
Konrad Rudolph
3

Aus dem C ++ 11-Standard:

Jeder C-Header, von dem jeder einen Namen mit dem Formularnamen.h hat, verhält sich so, als ob jeder Name, der durch den entsprechenden cname-Header im Standardbibliotheks-Namespace platziert wird, innerhalb des globalen Namespace-Bereichs platziert wird. Es ist nicht spezifiziert, ob diese Namen zuerst im Namespace-Bereich (3.3.6) des Namespace-Standards deklariert oder definiert werden und dann durch explizite using-Deklarationen (7.3.3) in den globalen Namespace-Bereich eingefügt werden.

Wenn Sie also verwenden <cstdio>, können Sie sicher sein, dass printfsich das im namespace stdund damit nicht im globalen Namespace befindet.
Die Verwendung eines globalen Namespace führt zu einem Namenskonflikt. Dies ist kein C ++ - Weg.

Daher verwende ich <cstdio>Header und rate Ihnen dazu.

NeonMercury
quelle
4
Obwohl ich wünschte, es hätte so funktioniert, ist dies nicht wahr. Wenn Sie einschließen, wird <cstdio>Ihnen garantiert, dass std :: printf vorhanden ist, es gibt jedoch keine Garantie des Standards, wenn :: printf ebenfalls vorhanden ist oder nicht. In der Tat, in jedem Compiler habe ich je von :: printf gehört wird in den globalen Namensraum eingespritzt , wenn Sie umfassen <cstdio>.
wjl
3

Aus meiner eigenen Praxis: std::Präfixe verwenden. Andernfalls abs wird Sie eines Tages sehr schmerzhaft beißen, wenn Sie Gleitkommazahlen verwenden.

Nicht qualifiziert absbezieht sich auf Funktionen, die intauf einigen Plattformen definiert sind. Bei anderen ist es überlastet. Ist std::absjedoch immer für alle Typen überlastet.

eiennohito
quelle
2

Die Verwendung von " printfohne" std::kann zu Namenskonflikten führen und wird von vielen C ++ - Entwicklern als schlechte Praxis angesehen. Google ist dein Freund in diesem Fall, aber hier sind einige Links, hoffe, das hilft

Warum wird "using namespace std" als schlechte Praxis angesehen? http://www.cplusplus.com/forum/beginner/61121/

Razvan
quelle
4
using namespace stdist eine schlechte Praxis, aber printfohne std::Qualifikation nicht.
Syntagma
using namespace std;ist hier nicht mein Problem. Ich benutze es nie. printf();und std::printf();arbeite in C ++ ohne using namespace std;Deshalb habe ich die Frage gepostet.
DeiDei
@ REACHUS nicht einverstanden. Es gibt keinen Unterschied zwischen den beiden Szenarien.
Konrad Rudolph
Ich würde es nie benutzen, std::printfes fühlt sich einfach seltsam an.
Trenki
@KonradRudolph Ich habe nicht gesagt, dass es einen Unterschied gibt, ich habe nur meine Meinung geäußert (siehe meine Antwort für weitere Gründe).
Syntagma
2

In stdio

Dies ist die C ++ - Version des Standard C Library-Headers @c stdio.h, und sein Inhalt ist (meistens) der gleiche wie dieser Header, ist jedoch alle im Namespace @c std enthalten (mit Ausnahme von Namen, die in als Makros definiert sind) C).

Es sollte also keinen Unterschied machen.

Anukul
quelle