2010-09-09 1 views
12

Beachten Sie die folgende C++ Code:Warum erlaubt eine Funktionsdeklaration mit einem const-Argument den Aufruf einer Funktion mit einem nichtkonstanten Argument?

#include <iostream> 
using std::cout; 

int foo (const int); 

int main() 
{ 
    cout << foo(3); 
} 

int foo (int a) 
{ 
    a++; 
    return a; 
} 

Beachten Sie, dass der Prototyp foo() eine const int nimmt und dass die Definition nimmt eine int. Diese kompilieren ohne Fehler ...

Warum gibt es keine Kompilierungsfehler?

+0

Bitte schauen zu Zeiger. – alternative

+14

@mathepic: Was hat dieser Code mit Zeigern zu tun? – casablanca

+0

@casablanca Alles - Es scheint, dass er const int und int wie int und int * vermischt. – alternative

Antwort

31

Da spielt es für den Aufrufer der foo Funktion keine Rolle, ob foo seine Kopie der Variablen ändert oder nicht.

Insbesondere in der C++ 03-Standard, werden die folgenden 2-Schnipsel genau erklären, warum:

C++ 03 Sektion: 13.2-1

Zwei Funktionsdeklarationen des gleichen Namens beziehen zur gleichen Funktion, wenn sie im gleichen Umfang sind und haben äquivalente Parameter Deklarationen (13.1).

C++ 03 Section: 13.1-3

Parameterdeklarationen, die nur in Gegenwart oder Abwesenheit von const unterscheiden und/oder flüchtig sind äquivalent. Nur die Konstanten und flüchtigen Typspezifizierer auf der äußersten Ebene der Parametertypspezifikation werden auf diese Weise ignoriert; Konstante und flüchtige Typspezifizierer, die innerhalb einer Parametertypspezifikation vergraben sind, sind signifikant und können verwendet werden, um überladene Funktionsdeklarationen zu unterscheiden.

+0

+1 für die Spezifikation angeben. – casablanca

+2

+1: Das zweite Zitat kann leicht mißverstanden werden, wenn man nicht folgt, was im Standard folgt: "Nur die Konstanten und flüchtigen Typspezifizierer auf der äußersten Ebene der Parametertypspezifikation werden auf diese Weise ignoriert; konstante und flüchtige Typbezeichner begraben innerhalb einer Parametertypspezifikation sind signifikant und können verwendet werden, um überladene Funktionsdeklarationen zu unterscheiden.112) ". Also 'int const * p' und 'int volatile * p' sind unterschiedlich – Chubsdad

+0

@Chubsdad: Zustimmen Ich hätte mehr dort hinlegen müssen, fügte es hinzu. Vielen Dank. –

-2
int foo (const int a) 
{ 
    a++; 
    return a; 
} 

Das wird einen Fehler beim Kompilieren werfen.

+1

Es wird, aber was hat das mit der Frage zu tun? –

12

Oberste Ebene const (d. H. Das gilt für den Wert, der übergeben wird, nicht etwas, auf das er zeigt oder verweist) betrifft nur die Implementierung, nicht die Schnittstelle einer Funktion. Der Kompilierer ignoriert es vom Standpunkt der Schnittstelle (d. H. Der aufrufenden Seite) und erzwingt es nur bei der Implementierung (d. H. Code im Körper der Funktion).

+5

+1 für * not * zitiert die Spezifikationen und stattdessen zu einem offensichtlich unerfahrenen Benutzer zu erklären, was das Grundprinzip hinter diesem Verhalten ist. –

+0

Ich sehne mich nach der zitierten Norm, solange das Zitat sinnvoll aus dem Kontext gerissen ist - es ist schließlich der entscheidende Punkt. – Steve314

+3

@ Steve314: Wie * wage * du meinst, dass meine Aussage etwas weniger definitiv ist, als den Standard zu zitieren! :-) –

2

Wie andere haben erklärt, sagt der Norm, es ist ok, und dass der Compiler kann es sich leisten nachsichtig über die Durchsetzung dieser sein, weil sie nicht den Anrufer nicht beeinflusst, aber niemand beantwortet hat, warum der Compiler sollte wählen sein nachsichtig. Es ist im Allgemeinen nicht besonders nachsichtig, und ein Programmierer, der gerade die Schnittstelle betrachtet und dann in die Implementierung eintaucht, kann es im Hinterkopf haben, dass ein Parameter const ist, wenn es nicht ist oder umgekehrt - keine gute Sache.

Diese Kronzeugenregelung ermöglicht Implementierungsänderungen, ohne die Header zu ändern. Durch die Verwendung traditioneller make-Tools wird die Neukompilierung von Clientcode ausgelöst. Dies kann ein ernstes Problem bei der Entwicklung im Enterprise-Bereich sein, wo eine nicht substantielle Änderung in einem Low-Level-Header (zB Logging) den Wiederaufbau von nahezu allen Objekten zwischen ihm und den Anwendungen erzwingen kann, was tausende Stunden CPU-Zeit verschwendet Verzögerung für alle und alles, was auf den Builds wartet.

Also, es ist hässlich, aber eine praktische Konzession.

Ich habe beantwortet auch eine ähnliche Frage, die sich auf, warum eine Überlastung von f (const T) und f (T) ist nicht erlaubt aussieht - dies jemand Lesen zu interessieren könnte - Top-level const doesn't influence a function signature

+0

Ich sehe es nicht als Leniancy. Ich kann eine Deklaration und eine Zuweisung 'const int x = y;' schreiben, auch wenn y zufällig eine nicht-konstante Variable ist. Es ist wichtig, dass der Wert von x in seiner Lebenszeit konstant bleibt - nicht dass der Wert von y ebenfalls konstant bleibt. Ich kann auch »int x = y;« schreiben, auch wenn es sich um eine Konstante handelt.Die Parameterübergabe macht fast dasselbe. Es ist nicht Leniancy, weil es keine Regel gibt, die beim Erzwingen leninant ist - der formale Parameter erhält durch den Aufruf einen Wert (keine Variable), und es gibt keinen Grund, warum die Quelle dieses Wertes nicht modifizierbar sein sollte. – Steve314

+0

@Steve: Der Kronzeugenaspekt bezieht sich auf das Akzeptieren einer Deklaration, die besagt, dass Sie den Parameter ändern können oder nicht, und dann eine Definition, die nicht übereinstimmt. Wie Sie sagen, wäre beides in Ordnung, aber das bedeutet nicht, dass sie gleichwertig oder konsistent sind. –

+0

In meiner Denkweise tut es. Ein Wert hat einfach kein 'const' - es ist eine Eigenschaft von Variablen, Mitgliedern, Parametern usw., aber nicht von Werten. Werte sind immer unveränderlich - 5 ist immer 5 und so weiter. Diese einfache Idee ist sehr gut, um vorherzusagen, wie sich C++ verhalten wird, egal ob Sie Variableninitialisierungen, Parameter, Überladung oder was auch immer betrachten. In jedem Fall verhalten sich Parameter immer sehr ähnlich wie lokale Variablen - nur die Initialisierungsmethode unterscheidet sich von der obigen Regel. Das Überladen, um sich um die Const der obersten Ebene zu kümmern, scheint mir sehr widersprüchlich zu sein. – Steve314