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"
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.”
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With