Is there any difference between:
void (* const algorithm)();
and
void const (* const algorithm)();
when dealing with const pointers to static methods?
I understand that it would make sense to use const if the pointer points to memory that should not be modified, in case of pointers to variables, as stated in this answer. However, are function addresses not effectively constant during run-time anyway?
The reason I am asking this is, the second option as a function parameter does not work.
EDIT
Here is the code that does not compile.
struct A {
static void a() {}
};
void b(void const (* const callback)()) {}
int main() {
b(&A::a); // No matching function for call to 'b'
}
The above example works, if function a() has a return type const void.
const after (or before) the return type applies to the return type. There is no such thing as a "pointer to const function" as there is no such thing as a const function. (Although, there is such thing as a pointer to const member function, as const member functions do exist. But there the constness applies to the object argument, not to the function itself. There the constness is expressed in the same way as in the declaration of a member function - after the parenthesized parameter list).
There is no difference between void() and void const() in the sense that both functions behave exactly the same because there is no behavioral difference between a const void return type and a non-const void return type. Object cannot be const when there is no object.
I would expect a compiler to issue a warning when a non-class return value is directly qualified with const.
However, void() and void const() are different in the sense that void and void const are technically separate types, as are all const qualified types different from their non-const counterpart. Therefore the function pointers to const returning and non-const returning functions are different function pointer types. As such, the standard won't allow a function pointer to one type be bound to a function of another type.
So, to fix your non-compiling code, simply replace void const which is nonsensical with void in the function pointer.
Start with the "inner" const :
using void_function = void();
void (*p1)() = nullptr; // similar to `void_function* p1 = nullptr;`
void (* const p2)() = nullptr; // similar to `void_function* const p2 = nullptr;`
p2 is constant whereas p1 is mutable.
When moving const, as following:
const void_function* p3 = nullptr; // or void_function const* p3; // -> const has no effect
void (const* p4)() = nullptr; // Invalid syntax
There are no "const function" vs "mutable function".
Now, looking at function return type:
Types void () (function returning void) and const void () (function returning const void !?) are different.
Even if const void make no real sense, it is a valid type.
It might make sense to return const object from function to disallow "direct" modification of the object:
const std::string make_const_string();
make_const_string().push_back('*'); // Doesn't work
std::string s = make_const_string(); // OK, create mutable copy.
So to fix your code:
struct A {
static void a() {}
};
void b(void const (* const callback)()) {}
int main() {
b(&A::a); // No matching function for call to 'b'
}
You have to make &A::a match b's argument type:
static const void a() {}
void b(void (*const callback)())
I suggest second one as const void make no real sense.
Related
This question already has answers here:
Top-level const doesn't influence a function signature
(7 answers)
const qualifier disappears from pure virtual function [duplicate]
(1 answer)
Closed 4 years ago.
A C++ guru told that varying the function parameter type with const in derived class, will break the virtual call mechanism.
I tried a simple program ( forgive for non standard code, written purely for test) , which prove otherwise.
The function parameter change by const value will not break the virtual mechanism,
Is there any reasons & documentation pointing to this behavior?
Behavior noted with VS 2012 compiler & latest g++ compiler.
#include <iostream>
using namespace std;
class Base
{
public:
Base(){ cout<<"base"<<endl;}
virtual ~Base(){ cout<<"dest base"<<endl;}
virtual void test(const int x){ cout << "base test"<<"x = " << x<<endl;}
};
class Derived : public Base
{
public:
Derived(){ cout<<"derived"<<endl;}
virtual ~Derived(){ cout<<"dest derived"<<endl;}
virtual void test(int x){ cout << "derived test"<<"x = " << x<<endl;}
};
int main() {
Base *b = new Derived();
b->test(10);
delete b;
return 0;
}
output:
base
derived
derived testx = 10
dest derived
dest base
The top level cv-qualifier is not part of function signature, they are simply ignored.
[dcl.fct]/5
After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type.
Your C++ guru is wrong (provided you understood them, gurus tend to talk in cryptic messages). Const-qualifier on the argument type itself is not a part of a function signature at all.
For example, void foo(int* const ); is no different from void foo(int* ). Please note, it is not the same as const qualification of the indirect object, like void foo(const int* ) is different from void foo(int* ).
In your particular case, void test(int x) is the same as void test(int const x)
void test(int) != void test(int) const and would "break" virtual call.
and void test(int&) != void test(const int&) and would "break" virtual call.
void test(int) is the same declaration than void test(const int) and won't "break" virtual call.
The equivalent of std::decay happens to argument types for functions. (It's actually the reverse, std::decay is modeled after what functions arguments do.)
The outermost const will be dropped from the signature. By outermost, think of types as envelopes composed over different types. A pointer to const int is a different type than a pointer to int, and will result in a different function signature. (With a pointer, you can think of the pointer itself as the outer thing, and what it points to is "inner" and not modified.)
const int - becomes just int
int * is unchanged and remains int *
const int * is unchanged and remains const int * - const is on the int, not the pointer, and only the outermost const is dropped
int const * is unchanged and remains int const * - const is on the int, not the pointer, and only the outermost const is dropped. (Note, this is 100% identical in meaning as const int *)
int * const - changes to int * - because const qualifies the outermost pointer
int * const * const becomes int * const * - outer const is dropped on outer pointer, inner const is not dropped on the inner pointer.
const int * const * const becomes const int * const * - outer const is dropped on outer pointer, inner const is not dropped on the inner pointer, and const is also kept on the internal-most int
MyTemplate<const T> - unchanged, remains MyTemplate<const T>, because the const isn't on the outer type, but nestled in a template parameter
So yes, const does affect type, but not in the simple case like you tried. Only when it's contained inside of a type, not affecting the outermost type.
If you read types from right-to-left it can help. If the rightmost thing in a type specifier is a const, it is always dropped (example int * const). If the leftmost thing is const, it is only dropped if the thing it is qualifying is the rightmost thing in the type (example const int, the leftmost thing is const and it affects the int to its right AND the int to its right is the rightmost thing in the type.) (example 2: const * int not dropped, because leftmost const modifies the thing to its right that is not the rightmost thing in the type.)
He was right.just found it by seeing the warning.
In this case : Compilers before VS2008 would break the virtual mechanism in this case.
Later compilers it gives warning C4373: virtual function overrides '%$pS', previous versions of the compiler did not override when parameters only differed by const/volatile qualifiers
found the documentation here https://msdn.microsoft.com/en-us/library/bb384874.aspx
This question already has answers here:
Top-level const doesn't influence a function signature
(7 answers)
const qualifier disappears from pure virtual function [duplicate]
(1 answer)
Closed 4 years ago.
A C++ guru told that varying the function parameter type with const in derived class, will break the virtual call mechanism.
I tried a simple program ( forgive for non standard code, written purely for test) , which prove otherwise.
The function parameter change by const value will not break the virtual mechanism,
Is there any reasons & documentation pointing to this behavior?
Behavior noted with VS 2012 compiler & latest g++ compiler.
#include <iostream>
using namespace std;
class Base
{
public:
Base(){ cout<<"base"<<endl;}
virtual ~Base(){ cout<<"dest base"<<endl;}
virtual void test(const int x){ cout << "base test"<<"x = " << x<<endl;}
};
class Derived : public Base
{
public:
Derived(){ cout<<"derived"<<endl;}
virtual ~Derived(){ cout<<"dest derived"<<endl;}
virtual void test(int x){ cout << "derived test"<<"x = " << x<<endl;}
};
int main() {
Base *b = new Derived();
b->test(10);
delete b;
return 0;
}
output:
base
derived
derived testx = 10
dest derived
dest base
The top level cv-qualifier is not part of function signature, they are simply ignored.
[dcl.fct]/5
After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type.
Your C++ guru is wrong (provided you understood them, gurus tend to talk in cryptic messages). Const-qualifier on the argument type itself is not a part of a function signature at all.
For example, void foo(int* const ); is no different from void foo(int* ). Please note, it is not the same as const qualification of the indirect object, like void foo(const int* ) is different from void foo(int* ).
In your particular case, void test(int x) is the same as void test(int const x)
void test(int) != void test(int) const and would "break" virtual call.
and void test(int&) != void test(const int&) and would "break" virtual call.
void test(int) is the same declaration than void test(const int) and won't "break" virtual call.
The equivalent of std::decay happens to argument types for functions. (It's actually the reverse, std::decay is modeled after what functions arguments do.)
The outermost const will be dropped from the signature. By outermost, think of types as envelopes composed over different types. A pointer to const int is a different type than a pointer to int, and will result in a different function signature. (With a pointer, you can think of the pointer itself as the outer thing, and what it points to is "inner" and not modified.)
const int - becomes just int
int * is unchanged and remains int *
const int * is unchanged and remains const int * - const is on the int, not the pointer, and only the outermost const is dropped
int const * is unchanged and remains int const * - const is on the int, not the pointer, and only the outermost const is dropped. (Note, this is 100% identical in meaning as const int *)
int * const - changes to int * - because const qualifies the outermost pointer
int * const * const becomes int * const * - outer const is dropped on outer pointer, inner const is not dropped on the inner pointer.
const int * const * const becomes const int * const * - outer const is dropped on outer pointer, inner const is not dropped on the inner pointer, and const is also kept on the internal-most int
MyTemplate<const T> - unchanged, remains MyTemplate<const T>, because the const isn't on the outer type, but nestled in a template parameter
So yes, const does affect type, but not in the simple case like you tried. Only when it's contained inside of a type, not affecting the outermost type.
If you read types from right-to-left it can help. If the rightmost thing in a type specifier is a const, it is always dropped (example int * const). If the leftmost thing is const, it is only dropped if the thing it is qualifying is the rightmost thing in the type (example const int, the leftmost thing is const and it affects the int to its right AND the int to its right is the rightmost thing in the type.) (example 2: const * int not dropped, because leftmost const modifies the thing to its right that is not the rightmost thing in the type.)
He was right.just found it by seeing the warning.
In this case : Compilers before VS2008 would break the virtual mechanism in this case.
Later compilers it gives warning C4373: virtual function overrides '%$pS', previous versions of the compiler did not override when parameters only differed by const/volatile qualifiers
found the documentation here https://msdn.microsoft.com/en-us/library/bb384874.aspx
For a function definition which contains the declaration of type void foo(const int ) Both of the following declarations are valid.
void foo(const int); // valid
void foo(int); // valid, const can be omitted.
But if a function definition contains a declaration of type void foo(const int*) omitting const is ilegal:
void foo(const int *); // valid declaration
void foo(int *); // error: const cannot be omitted.
Why is const cannot be omitted in the function declaration if its parameter has pointer types? What makes the difference?
You can only omit the const specifier when it's applied directly to the parameter. In the pointer case, it's applied to the thing being pointed at, not the pointer itself, so there's an extra level of indirection between const and the parameter. You could omit it in this case:
void foo(int* const);
void foo(int*);
const int x means that x is read-only inside the function. The outside world doesn't care either way; the function is working with a copy of the argument.
However const int *p means that p points to something that is read-only. The outside world does care about this; it's a promise that the pointee can't be modified.
The corollary of this is that the following two declarations are equivalent:
void foo(const int *);
void foo(const int * const);
Function deals with the copy of the passed argument. If that's integer, you can omit const as original will not be touched anyway. In case of the pointer to int type there are three options:
int* p
const int* p (or int const* p)
const int* const p (or int const* const p)
const can be applied to pointed object, pointer itself or to both. What you pass as an argument is pointer so you can omit const for it as its copy is passed to function. So (2) and (3) can be used interchangeably (only when used as function argument type!). But const for pointed object type makes difference:
void foo(const int* p); // function cannot modify pointed integer object through p
void foo(int* p); // function can modify pointed integer object through p
In both cases function can modify p within function, but those changes are not reflected on the original pointer's value.
if this is a const pointer to class's object how can you return a const pointer from non-const return type?
Class T
{
public:
T* func(){return this;}
};
Firstly, this is not a "const pointer". Where did you get that strange idea? Pointer this has scalar type and is not an lvalue, meaning that it can't possibly be "const" or "non-const". Pointer this is an rvalue and it is non-modifiable.
Secondly, the code in your question is valid regardless of whether the pointer involved is const or not. For example, the following code is valid for exactly the same reason
int *const p = 0; /* a const pointer */
int *foo() {
return p; /* OK, no error here */
}
What is returned in this case is a completely independent copy of the original pointer value. It does not matter whether p is const or not - making a copy of const value does not in any way violate its constness.
This is exactly what's done in your code sample - you are returning a copy of this's value. Why would you even care whether this is const or not?
Unless the member function is const-qualified (e.g. T* func() const instead of just T* func()), this is of type T*.
this is not itself modifiable (so you can't assign something to this), but it's just a pointer to an object of type T like any other and can be used as such.
AndreyT provided the explanation and here is the reference.
From standard docs 9.3.2.1 The this pointer,
In the body of a non-static (9.3) member function, the keyword this is an rvalue expression whose value is the address
of the object for which the function is called. The type of this in a member function of a class X is X*. If the member
function is declared const, the type of this is const X*, if the member function is declared volatile, the type of
this is volatile X*, and if the member function is declared const volatile, the type of this is const volatile
X*.
which explains the question that you asked and the comment you made to AndreyT.
Hope this one also helps.
A pointer is not a object, so it does not matter if the pointer is const when it is a return value.
Consider this:
int x;
int f() {
return x;
}
const int g() {
return x;
}
There's actually no difference between f() and g().
But it may make difference if you replace all "int" to some other name of a class.
"const" may prevent you from some error, e.g. when you don't write a copying constructor properly.
class T
{
int *x;
public:
T() {x = new int;}
void set_x(int n) {*x = n;}
int get_x() const {return *x;}
};
T a;
T f() {
return a;
}
const T g() {
return a;
}
int main() {
f().set_x(123); // The value is modified
// even though a return value is not a l-value.
g().set_x(123); // Compile Error
}
So, if you want to prevent modifying data of the object by referring to the return value, what you need is:
class T
{
public:
const T* func() const
{ return this; }
};
Please consider the following code.
struct foo
{
};
template<typename T>
class test
{
public:
test() {}
const T& value() const
{
return f;
}
private:
T f;
};
int main()
{
const test<foo*> t;
foo* f = t.value();
return 0;
}
t is a const variable and value() is a constant member-function which returns const T&. AFAIK, a const type is not assignable to a non-const type. But how foo* f = t.value(); compiles well. How this is happening and how can I ensure value() can be only assigned to const foo*?
Edit
I found that, this is happening on when templates are used. Following code works as expected.
class test
{
public:
test() {}
const foo* value() const { return f; }
private:
foo* f;
};
int main()
{
const test t;
foo* f = t.value(); // error here
return 0;
}
Why the problem is happening when templates are used?
Because you have two levels of indirection - in your main function, that call to value returns a reference to a const pointer to a non-const foo.
This can safely be copied into non-const pointer to a non-const foo.
If you'd instantiated test with const foo *, it would be a different story.
const test<const foo*> t;
foo* f = t.value(); // error
const foo* f = t.value(); // fine
return 0;
Update
From the comment:
value() returns const T& which can
only be assigned to another const
type. But in this case, compiler is
safely allowing the conversion.
Const data can only be read. It cannot be written ("mutated"). But copying some data is a way of reading it, so it's okay. For example:
const int c = 5;
int n = c;
Here, I had some const data in c, and I copied the data into a non-const variable n. That's fine, it's just reading the data. The value in c has not been modified.
Now, suppose your foo had some data in it:
struct foo { int n; };
If I have a non-const pointer to one of those, I can modify the n value through the pointer. You asked your test template to store a pointer to a non-const foo, and then made a const instance of test. Only the pointer address is constant, therefore. No one can change the address stored in the pointer inside test, so it cannot be made to point to another object. However, the object it points to can have its contents modified.
Update 2:
When you made your non-template version of the example, you made a mistake. To get it right, you need to substitute foo * into each place where there's a T.
const T& value() const
Notice that you have a reference to a const T there. So the return value will be a reference to something const: a foo *. It's only the pointer address that can't be modified. The object it points to can have its contents modified.
In your second example, you got rid of the reference part, which changes the meaning and makes the const modifier apply to the object that the pointer points to, instead of applying to the pointer itself.
Use the following template specialization:
template<typename T>
class test<T*>
{
public:
test() {}
const T* value() const
{
return f;
}
private:
T* f;
};
After including this, g++ says:
d.cpp: In function ‘int main()’:
d.cpp:41: error: invalid conversion from ‘const foo*’ to ‘foo*’
There's nothing wrong in your code, having a const reference to a pointer only means that you can't modify the pointer, but the pointed-to object remains perfectly mutable. If inside your main function you try to change the address pointed to by the f member of t you'll see that you can't: encapsulation is perfectly preserved.
This is the same principle that makes the following code valid:
void foo(std::vector<int *> const & v)
{
*v[0] = 0; // op. [] returns const & to int *
}
People new to C++ are usually surprised by this behavior, because for them a const vector should not allow the modification of its elements. And in fact it doesn't, because the pointer stored in the vector does not change (it keeps pointing to the same address). It's the pointed-to object which is modified, but the vector does not care about that.
The only solution is to do as Amit says and provide a specialization of your class for T*.