Type of 'this' pointer
The type of this pointer is either ClassName *
or const ClassName *
, depending on whether it is inspected inside a non-const or const method of the class ClassName
. Pointer this
is not an lvalue.
class ClassName {
void foo() {
// here `this` has `ClassName *` type
}
void bar() const {
// here `this` has `const ClassName *` type
}
};
The observation you mentioned above is misleading. Pointer this
is not an lvalue, which means that it cannot possibly have ClassName * const
type, i.e. it cannot possible have a const
to the right of the *
. Non-lvalues of pointer type cannot be const or non-const. There's simply no such concept in C++ language. What you observed must be an internal quirk of the specific compiler. Formally, it is incorrect.
Here are the relevant quotes from the language specification (emphasis mine)
9.3.2 The this pointer
In the body of a non-static (9.3) member function, the keyword this is
a prvalue 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*. [ Note: thus in
a const member function, the object for which the function is called
is accessed through a const access path. —end note ]
It is worth nothing that back in the C++98/C++03 times several compilers used an internal implementational trick: they interpreted their this
pointers as constant pointers, e.g. ClassName *const
in a non-constant method of class ClassName
. This apparently helped them to ensure non-modifiablity of this
. GCC and MSVC are known to have used the technique. It was a harmless trick, since at language level this
was not an lvalue and its constness was undetectable. That extra const
would generally reveal itself only in diagnostic messages issued by the compiler.
However, with the advent of rvalue references in C++11 it became possible to detect this extra const
on the type of this
. For example, the following code is valid in C++11
struct S
{
void foo() { S *&&r = this; }
};
Yet it will typically fail to compile in implementations that still use the aforementioned trick. GCC has since abandoned the technique. MSVC++ still uses it (as of VS2017), which prevents the above perfectly valid code from compiling in MSVC++.
How can I get the type of the variable a pointer points to?
So it seems that there is no real solution to this other than using virtual functions.
In C++, why is mentioning the type of the pointer necessary?
If you declare b
as auto
, the compiler will use type inference and determine the type itself. But if you use a specific type, it has to be the correct one.
the type of this* in C++
For beginners, this
is often depicted as a constant pointer.
However, this
is actually a prvalue (pure rvalue) of pointer type. You can't assign anything to prvalues of fundamental type, which implies the "const-ness" of this
.
The exact type of this
depends on the cv-qualification of the method. A rule of thumb is that the cv-qualification is simply prepended to the usual pointer type - i.e., if a method of Class
is marked const
, then the type is const
Class*
.
if the compiler can compile it fine whether there is a 'const' or
not,why does it matter?
If (and only if) the pointee type of this
is const
, you can't modify the members of the class.
Class const* ptr; // ptr->data is also const, not modifiable through this pointer
Class* ptr; // ptr->data isn't const - can be modified.
The const
-qualifier on methods allows you to distinguish between methods for const
objects and methods for non-const
ones, which is often a necessity.
Why do you have to specify a type for pointers?
You don't have to specify a type for pointers. You can use void*
everywhere, which would force you to insert an explicit type cast every single time you read something from the address pointed by the pointer, or write something to that address, or simply increment/decrement or otherwise manipulate the value of the pointer.
But people decided a long time ago that they were tired of this way of programming, and preferred typed pointers that
- do not require casts
- do not require always having to know the size of the pointed type (which is an issue that gets even more complicated when proper memory alignment has to be taken into consideration)
- prevent you from accidentally accessing the wrong data type or advancing the pointer by the wrong number of bytes.
And yes, indeed, all data pointers, no matter what their type, occupy the same amount of memory, which is usually 4 bytes on 32-bit systems, and 8 bytes on 64-bit systems. The type of a data pointer has nothing to do with the amount of memory occupied by the pointer, and that's because no type information is stored with the pointer; the pointer type is only useful to humans and to the compiler, not to the machine.
Incompatible pointer type warning with pointer-to-pointer types when passing char** to void** function parameter
C lets any pointer be implicitly cast to void*
as an explicit exception. Note, that void
and char
are not compatible types. Thus void*
, char*
, void**
and char**
are not compatible as well. That is why compiler emits a warning.
To bypass this issue change the function signature to use void*
:
void secure_free(void* ptr, size_t length_in_bytes) {
void **p_p_data = (void**)ptr;
...
}
To add protection that the argument is a pointer to a pointer one could use a macro:
#define secure_free(x,s) ((void)sizeof **(x), secure_free((x), (s)))
- Expression
**(x)
will not compile is x were not a pointer to a pointer. sizeof
prevent evaluation ofx
in**(x)
to avoid side effects(void)
silence the compiler about complaining on unused value- comma operator
(X,Y)
, return only value of theY
, which is return value ofsecure_free(...)
- using the same name for macro as for function allows to expand
secure_free
as macro only if it used as a function. This allows to usesecure_free
as a pointer to a function
Extra note. In the code
memset(*p_p_data, 0, length_in_bytes);
free(*p_p_data);
The compiler will likely optimize out memset()
. I suggest casting to volatile void *
to force the compiler to generate clearing code.
Edit
Additionally, one may have clear the content with a loop because memset
discards volatile
qualifier.
C getting type of a pointer
You can perform sort-of-overloading on types using _Generic macro if you are using a C11 compliant compiler, but the type has to be stored separately, be it a void*
or an union with some sort of tag.
The closest to what you want is sth like this:
#include <stdio.h>
#include <assert.h>
struct TaggedUnion
{
void* contents;
/* union {int* i;char* c;} contents; */
enum Type {CHAR_TYPE, INT_TYPE} type;
};
struct TaggedUnion storeI(int* x)
{
struct TaggedUnion ret = {.contents =x, .type=INT_TYPE};
return ret;
}
struct TaggedUnion storeC(char* x)
{
struct TaggedUnion ret = {.contents =x, .type=CHAR_TYPE};
return ret;
}
#define storeTagged(x) _Generic((x),\
int*: storeI,\
char*: storeC)(x)
int* getInt(const struct TaggedUnion x)
{
return x.type == INT_TYPE ? x.contents : NULL;
}
char* getChar(const struct TaggedUnion x)
{
return x.type == CHAR_TYPE ? x.contents : NULL;
}
void fi(int* arg)
{
printf("i\n");
}
void fc(char* arg)
{
printf("c\n");
}
#define ff(x) _Generic((x),\
int*: fi,\
char*: fc)(x)
int main(int argc, const char* argv[])
{
printf("entry\n");
int* i;
char* c;
ff(i);
ff(c);
struct TaggedUnion ti = storeTagged(i);
struct TaggedUnion tc = storeTagged(c);
assert(ti.type == INT_TYPE);
assert(tc.type == CHAR_TYPE);
return 0;
}
https://godbolt.org/z/5dcrc84fo
Can a pointer to an incomplete type be incomplete?
An array of unknown size is incomplete:
An array type of unknown size is an incomplete type. It is completed, for an identifier of that type, by specifying the size in a later declaration (with internal or external linkage).
The type int (*)[]
however is not incomplete: It's a pointer of an array of int
of unknown size.
And a pointer has a well known size:
printf ("Size %d\n", sizeof(int (*)[]));
6.2.5/23: A type has known constant size if the type is not incomplete and is not a variable length array type.
Furthermore you can even dereference it, thanks to the array semantics:
typedef int (*T)[];
...
int a[10];
for (int i=0; i<10; i++) a[i]=i;
T p=a;
for (int i=0; i<10; i++) printf ("%d ",(*p)[i]);
printf ("\n");
Edit
In addition, a pointer is always a complete type. It's written black on white in 6.2.5/20:
A pointer type may be derived from a function type or an object type,
called the referenced type. A pointer type describes an object whose
value provides a reference to an entity of the referenced type. A
pointer type derived from the referenced type T is sometimes called
‘‘pointer to T’’. The construction of a pointer type from a referenced
type is called ‘‘pointer type derivation’’. A pointer type is a
complete object type.
pointer name does not name a type error
These:
front = NULL;
rear = NULL;
are assignment statements. And this:
Node *front = NULL, *rear = NULL;
is a declaration (definition, initialization) statement using in-class initializers.
Assignment statements are not allowed to appear in the class declaration body, whereas the initialization statements are.
Related Topics
Changing the Current Directory in Linux Using C++
Way Cross Compile C/C++ Code to Run on Windows, Linux, and MAC Os
Getting Mangled Name from Demangled Name
Is Right Shift Undefined Behavior If the Count Is Larger Than the Width of the Type
In Which Order Should Floats Be Added to Get the Most Precise Result
What Are the Rules for the "..." Token in the Context of Variadic Templates
C++: Simple Return Value from Std::Thread
Difference Between Try-Catch Syntax for Function
Linux C++ Error: Undefined Reference to 'Dlopen'
How to Compile SQLite with Icu
External Library Throws Undefined Reference Errors in Qt Creator
Format Number with Commas in C++
How to Catch the Null Pointer Exception
Correct Use of Std::Cout.Precision() - Not Printing Trailing Zeros