I had the following C++ code, where the argument to my constructor in the declaration had different constness than the definition of the constructor.
//testClass.hpp
class testClass {
public:
testClass(const int *x);
};
//testClass.cpp
testClass::testClass(const int * const x) {}
I was able to compile this with no warnings using g++, should this code compile or at least give some warnings? It turns out that the built-in C++ compiler on 64 bit solaris gave me a linker error, which is how I noticed that there was an issue.
What is the rule on matching arguments in this case? Is it up to compilers?
In cases like this, the const specifier is allowed to be ommitted from the declaration because it doesn't change anything for the caller.
It matters only to the context of the implementation details. So that's why it is on the definition but not the declaration.
Example:
//Both f and g have the same signature
void f(int x);
void g(const int x);
void f(const int x)//this is allowed
{
}
void g(const int x)
{
}
Anyone calling f won't care that you are going to treat it as const because it is your own copy of the variable.
With int * const x, it is the same, it is your copy of the pointer. Whether you can point to something else doesn't matter to the caller.
If you ommitted the first const though in const int * const, then that would make a difference because it matters to the caller if you change the data it is pointing to.
Reference: The C++ Standard, 8.3.5 para 3:
"Any cv-qualifier modifying a
parameter type is deleted ... Such
cv-qualifiers affect only the
definition of the parameter with the
body of the function; they do not
affect the function type"
This example is covered explicitly in the overload resolution section, 13.1/3b4:
Parameter declarations that differ only in the presence or absence of const and/or volatile are
equivalent. That is, the const and volatile type-specifiers for each parameter type are ignored
when determining which function is being declared, defined, or called.
[Example:
typedef const int cInt;
int f (int);
int f (const int); // redeclaration of f(int)
int f (int) { ... } // definition of f(int)
int f (cInt) { ... } // error: redefinition of f(int)
—end example]
So, it is definitely OK.
Think of it as the same difference between
//testClass.hpp
class testClass {
public:
testClass(const int x);
};
//testClass.cpp
testClass::testClass(int x) {}
Which also compiles. You can't overload based on the const-ness of a pass-by-value parameter. Imagine this case:
void f(int x) { }
void f(const int x) { } // Can't compile both of these.
int main()
{
f(7); // Which gets called?
}
From the standard:
Parameter declarations that differ
only in the presence or absence of
const and/or volatile are equivalent.
That is, the const and volatile
type-specifiers for each parameter
type are ignored when determining
which function is being declared,
defined, or called. [Example:
typedef const int cInt;
int f (int);
int f (const int); // redeclaration of f(int)
int f (int) { ... } // definition of f(int)
int f (cInt) { ... } // error: redefinition of f(int)
—end example] Only the const and
volatile type-specifiers at the
outermost level of the parameter type
specification are ignored in this
fashion; const and volatile
type-specifiers buried within a
parameter type specification are
significant and can be used to
distinguish overloaded function
declarations.112) In particular,
for any type T, “pointer to T,”
“pointer to const T,” and “pointer to
volatile T” are considered distinct
parameter types, as are “reference to
T,” “reference to const T,” and
“reference to volatile T.”
Is const int * const x
not the same like const int * x
becouse u already maked const?