Explicitly passing *this in C++ - c++

While reading about *this, I saw:
When a nonstatic member function is called for an object, the compiler
passes the object's address to the function as a hidden argument.
Then I tried:
#include <iostream>
class MyClass
{
int myVar;
public:
MyClass(const int& val) : myVar{val} {}
// int getVar(MyClass* this) <-- Error: expected ',' or '...' before 'this'
int getVar()
{
return this->myVar;
}
};
int main()
{
MyClass obj(22);
// std::cout << obj.getVar(&obj); <-- Error: no matching function
// std::cout << MyClass::getVar(&obj); <-- Error: no matching function
std::cout << obj.getVar();
return 0;
}
Why am I not able to access the hidden argument? Is it called 'hidden' because of that?
Are only compilers allowed to do this? Can't we explicitly mention *this in the function signature?
The closest answer I've found before asking this is this. But I tried that way and still got the error. Could I get an explanation of those error messages? Because, if the compiler actually modifies those function signatures to contain *this then that should have worked, isn't it?

Are only compilers allowed to do this?
Precisely. That's why it's called hidden: It's something that the compiler does on your behalf, but which is hidden from the C++ code that uses it.
The compiler must pass the this pointer to the member function somehow, but it does not need to tell you how it does it. It could compile the code to the equivalent of MyClass::getVar(&obj), passing the this pointer in the same way that it would pass the argument for the C function free(foo). Or it might use a different mechanism that is totally incompatible with non-member argument passing. What it does under the hood is defined by the platform's Abstract Binary Interface standard (ABI), which is not part of the C++ language standard. What happens under Windows could be vastly different from what happens under Linux, and Linux on ARM could be different from Linux on X86, etc.
That said, you can take a look at what actually happens by telling your compiler to produce the assembly code. For gcc, the incantation would be
g++ -S -Os interestingCode.cpp
This will produce a .s file that contains how g++ actually translated your code.

obj.getVar(&obj)
This version cannot compile because the getVar() member function is not declared to take any parameters.
MyClass::getVar(&obj)
This version is using the syntax to access a static function but getVar() is not static, nor does it accept any parameters.
Note: The obj.getVar() call works because it is specifying which object instance to use (i.e., the obj. part) to execute the member function and is conceptually how the member function is passed the this pointer.

When you are doing obj.getVar() it is already explicitly specified the pointer *this=&obj and passed implicitly to getVar. It is not hidden. It is explicitly passed leftside of the function. You can use obj.getVar() or ptrObj->getVar() but in C++ is not allowed to use such construction getVar(thisptr). Hidden means the variable named this is nowhere declared, but you can use inside the function.

Related

Can variables be used in function call in ellipsis functions in C++

For this function that takes variable number of arguments,
void func(int count, ...) // ellipsis function
{
// function definition
}
Can a function call be made like follows :
int a{};
double b{};
string c{};
func(3,a,b,c); // using actual variables instead of fixed values in function call
My question is when an ellipsis function is called does it always has to be just fixed values like func(3,5,2.7,"Hi") or can variables be supplied in the function call like so func(3,a,b,c)?
Note that passing classes like std::string, with non-trivial copy constructor or nontrivial move constructor or non-trivial destructor, may not be supported and has "implementation-defined" semantics. You have to check your compiler documentation on how such classes are passed or check if they are supported at all.
Can variables be used in function call in ellipsis functions in C++
Yes.
Can a function call be made like follows
Yes.
when an ellipsis function is called does it always has to be just fixed values like func(3,5,2.7,"Hi")
No.
can variables be supplied in the function call like so func(3,a,b,c)?
Yes.
Can you suggest any reference so I can do some research on it?
https://en.cppreference.com/w/cpp/language/variadic_arguments https://en.cppreference.com/w/cpp/utility/variadic https://eel.is/c++draft/expr#call-12
And in C++ you should strongly prefer: https://en.cppreference.com/w/cpp/language/parameter_pack , because of type safety.
Though ellipsis gives us some useful functionality, it is quite dangerous to use them. When using ellipsis, the compiler does not check the type of arguments passed to the function. So the compiler does not throw any error if arguments are of different types. Even if pass string, double, or bool type values are passed to the average() function it returns return an unexpected value, the compiler does not throw any error.
Source : https://www.geeksforgeeks.org/ellipsis-in-c-with-examples/

Ambiguous call to overloaded static function

I'm confused by this situation and googling didn't give me the answer. Basically I have the following simple code that doesn't compile:
#include <iostream>
class A
{
public:
int a(int c = 0) { return 1; }
static int a() { return 2; }
};
int main()
{
std::cout << A::a() << std::endl;
return 0;
}
In compiling this, GCC 4.2 says the call to A::a() in main() is ambiguous with both versions of a() valid candidates. Apple's LLVM compiler 3.0 compiles with no error.
Why is gcc confused about which function I want to call? I thought it was obvious that by qualifying a() with A:: I'm asking for the static version of the function. Naturally this code still doesn't compile if I remove the static function a(), because A::a() is not valid syntax for calling the non-static a().
Thanks for any comment!
The reason for this is because C++ specifies that this is ambiguous. Overload resolution specifies that for A::a, since this is not in scope, the argument list in that call is augmented by a contrived A object argument, instead of *this. Overload resolution does not exclude non-static member functions, but instead
If the argument list is augmented by a contrived object and overload resolution selects one of the non-static member functions of T, the call is ill-formed.
This has recently been subject of extensive discussion both in the committee in context of core issue 1005. See core issue 364 which considered changing this rule but didn't do so.
The reason is name resolution happens before anything else the compiler does, like figuring out which overloaded function to use.
Qualifying the function with A:: simply tells the compiler to "look inside of A to find the name a". It doesn't actually help resolve which function you are referring to.
EDIT
And so when you type A::a(), first the compiler thinks "look in A for a member function or member who can use operator()".
Then the compiler thinks, "Ok, two possibilities here, which one is being referred to? a() or a(int c = 0) with a default c=0. Not sure.
If you removed the static keyword and called the functions like obj.a(), there would still be an ambiguity.
WRT LLVM's parser
I would say that it does some extra work for you, which is not required by the standard, which would be to assume A::a() is static.

decltype(function) as class member

I have:
int foo(int x) { return x+1; }
struct Bar {
decltype(foo) operator();
};
int main() {
Bar bar;
printf("%d\n",bar(6));
}
which results in the slightly startling compiler error message (g++ 4.6.1):
error: declaration of 'operator()' as non-function
When changing the member name to
decltype(foo) blubb;
and using it results in a linker error:
undefined reference to `Bar::blubb(int)'
Is this expected behaviour?
It seems that you want to "copy" the signature of another function to create a function with the same signature. Since decltype(foo) is indeed the type of the function (and not a pointer to that function, which would be decltype(&foo) and would lead to a pointer declaration), you can use it to declare a function with the same signature as another function.
As indicated by the linker error:
undefined reference to `Bar::blubb(int)'
this will already work fine with your compiler. However it seems that gcc did not yet fully implement this part of the standard, as it will not accept the syntax for the same thing with a function call operator. Clang btw. will happily accept it and the link then errors out with
undefined reference to `Bar::operator()(int)'
Your question about why that linker error exists indicates a misunderstanding of what decltype really does.
It will just evaluate to a type, not more. The definition of blubb is in no way tied to the definition of foo. This might be clearer when writing it like
typedef decltype(foo) x;
x blubb;
You can now alternatively typedef x to be explicitly the function type, which will not in any way change what blubb is. You still need to define it. And since there is no syntax to define it using decltype, you explicitly have to write it as
int Bar::operator()(int) {
...
}
which will likely and unfortunately defeat the purpose/benefit of using decltype for the declaration, as it will not allow you to automatically "copy" a signature.
This is a wild guess based on observing your usage of printf here:
printf("%d\n",bar(6));
This lets me assume you really want the return type of the function, not the type of the function. If so, then you use decltype wrong. You get the return type of the function by "simulating" the usage of the function, i.e.
decltype(foo(0)) operator() (int);
should be the right thing for you. Otherwise, if that was not your attention, you are skating on thin ice by giving a function type (and not function return type) to the %d specifier.
Generally, the meaning of decltype is: decltype(#) gives the static type of the expression #.
This should work.
I just used it here to capture whatever gobbledygook std::bind was going to give me:
class RiceFadingModel::Impl
{
public:
Impl(double K, double A)
: //...
_M_re{system_now()},
_M_rd{_M_nu, _M_sigma},
_M_gen{std::bind(_M_rd, _M_re)}
{ }
private:
//...
std::default_random_engine _M_re;
/// The underlying Rice distribution.
__gnu_cxx::__rice_distribution<double> _M_rd;
/// The variate generator built from the pseudo-random number engine and the Rice distribution.
decltype(std::bind(_M_rd, _M_re)) _M_gen;
};
This works like a charm on gcc-4.7. Now that I think about it I built it on mingw with gcc-4.5 too.

Nonstatic member as a default argument of a nonstatic member function [duplicate]

This question already has answers here:
How to use a member variable as a default argument in C++?
(4 answers)
Closed 1 year ago.
struct X
{
X():mem(42){}
void f(int param = mem) //ERROR
{
//do something
}
private:
int mem;
};
Can anyone give me just one reason as to why this is illegal in C++?! That is to say, I know that it is an error, I know what the error means, I just can't understand why would this be illegal!
Your code (simplified):
struct X
{
int mem;
void f(int param = mem); //ERROR
};
You want to use a non-static member data as default value for a parameter of a member function. The first question which comes to mind is this : which specific instance of the class the default value mem belongs to?
X x1 = {100}; //mem = 100
X x2 = {200}; //mem = 200
x1.f(); //param is 100 or 200? or something else?
Your answer might be 100 as f() is invoked on the object x1 which has mem = 100. If so, then it requires the implementation to implement f() as:
void f(X* this, int param = this->mem);
which in turn requires the first argument to be initialized first before initialization of other argument. But the C++ standard doesn't specify any initialization order of the function arguments. Hence that isn't allowed. Its for the same reason that C++ Standard doesn't allow even this:
int f(int a, int b = a); //§8.3.6/9
In fact, §8.3.6/9 explicitly says,
Default arguments are evaluated each
time the function is called. The order
of evaluation of function arguments is
unspecified. Consequently, parameters
of a function shall not be used in
default argument expressions, even if
they are not evaluated.
And rest of the section is an interesting read.
An interesting topic related to "default" arguments (not related to this topic though):
Default argument in the middle of parameter list?
Default arguments have to be known at compile-time. When you talk about something like a function invocation, then the function is known at compile-time, even if the return value isn't, so the compiler can generate that code, but when you default to a member variable, the compiler doesn't know where to find that instance at compile-time, meaning that it would effectively have to pass a parameter (this) to find mem. Notice that you can't do something like void func(int i, int f = g(i)); and the two are effectively the same restriction.
I also think that this restriction is silly. But then, C++ is full of silly restrictions.
As DeadMG has mentioned above, somethig like
void func(int i, int f = g(i))
is illegal for the same reason. i suppose, however, that it is not simply a silly restriction. To allow such a construction, we need to restrict evaluation order for function parameters (as we need to calculate this before this->mem), but the c++ standard explicitly declines any assumptions on the evaluation order.
The accepted answer in the duplicate question is why, but the standard also explicitly states why this is so:
8.3.6/9:
"
Example: the declaration of X::mem1() in the following example is ill-formed because no object is supplied for the nonstatic member X::a used as an initializer.
int b;
class X
int a;
int mem1(int i = a); // error: nonstatic member a
// used as default argument
int mem2(int i = b); // OK: use X::b
static int b;
};
The declaration of X::mem2() is meaningful, however, since no object is needed to access the static member X::b. Classes, objects and members are described in clause 9.
"
... and since there exists no syntax to supply the object necessary to resolve the value of X::a at that point, it's effectively impossible to use non-static member variables as initializers for default arguments.
ISO C++ section 8.3.6/9
a nonstatic member shall not be used in a default argument expression, even if it
is not evaluated, unless it appears as the id-expression of a class member access expression (5.2.5) or unless it is used to form a pointer to member (5.3.1).
Also check out the example given in that section.
For one reason, because f is public, but mem is private. As such, code like this:
int main() {
X x;
x.f();
return 0;
}
...would involve outside code retrieving X's private data.
Aside from that, it would (or at least could) also make code generation a bit tricky. Normally, if the compiler is going to use a default argument, it gets the value it's going to pass as part of the function declaration. Generating code to pass that value as a parameter is trivial. When you might be passing a member of an object (possibly nested arbitrarily deeply) and then add in things like the possibility of it being a dependent name in a template, that might (for example) name another object with a conversion to the correct target type, and you have a recipe for making code generation pretty difficult. I don't know for sure, but I suspect somebody thought about things like that, and decided it was better to stay conservative, and possibly open thins up later, if a good reason was found to do so. Given the number of times I've seen problems arise from it, I'd guess it'll stay the way it is for a long time, simply because it rarely causes problems.
Compiler has to know addresses to maintain default values at compile time. Addresses of non-static member variables are unknown at compile time.
As all the other answers just discuss the problem, I thought I would post a solution.
As used in other languages without default arguments (Eg C# pre 4.0)
Simply use overloading to provide the same result:
struct X
{
X():mem(42){}
void f(int param)
{
//do something
}
void f()
{
f(mem);
}
private:
int mem;
};
Default arguments are evaluated in two distinct steps, in different contexts.
First, the name lookup for the default argument is performed in the context of the declaration.
Secondly, the evaluation of the default argument is performed in the context of the actual function call.
To keep the implementation from becoming overly complicated, some restrictions are applied to the expressions that can be used as default arguments.
Variables with non-static lifetime can't be used, because they might not exist at the time of the call.
Non-static member variables can't be used, because they need an (implicit) this-> qualification, which can typically not be evaluated at the call site.

int transforms into int&?

code snippet:
// some code
SDL_Surface* t = Display->render_text(text);
int z=100;
Display->blit_image(t,z,100);
// some more code
does not compile because z magically changes to an int&,
file.cpp:48: error: no matching function for call to ‘display::blit_image(SDL_Surface*&, int&, int)
how can this happen?
post scriptum:
the following works if i put them in place of Display->blit_image(t,z,100)
Display->blit_image(t,z,100,0);
but i am sure that th 4th param is optional because the exact same function works elsewhere without it
pps: i created a minimal-case of my code that behaves as describd above
it's 3 files:
monkeycard.cpp: http://pastebin.com/pqVg2yDi
display.hpp: http://pastebin.com/xPKgWWbW
display.cpp: http://pastebin.com/nEfFX1wj
g++ -c display.cpp monkeycard.cpp fails with:
monkeycard.cpp: In member function ‘void monkeycard::messagebox(std::string)’:
monkeycard.cpp:28: error: no matching function for call to ‘display::blit_image(SDL_Surface*&, int&, int)’
display.hpp:26: note: candidates are: void display::blit_image(SDL_Surface*, int, int, SDL_Rect*)
The error message tells you what you're trying to pass. With automatic conversions and whatnot, that doesn't mean the function must have exactly that signature.
int& here just means that the parameter you've provided is an lvalue, and so it could be passed as a non-const reference. A function can match with that parameter as an int&, const int&, int, long, const float&, etc.
the point is that if instead of z i
write 100 it works.
That's interesting. I can't immediately think of a way to write a function that accepts an integer literal, but not an integer variable. The following code compiles, of course:
struct SDL_Surface;
struct SDL_Rect;
struct display {
void foo(SDL_Surface* img, int x=0, int y=0, SDL_Rect* clip=0) {}
};
int main() {
display d;
int z = 0;
SDL_Surface *p = 0;
d.foo(p,z,100);
}
So there must be something else you haven't mentioned yet, which causes the issue.
Edit: visitor and Charles Bailey (in a comment) have the answer. The defaults are missing from your declaration of the function, so as far as the compiler is concerned you are trying to call a 4-parameter function with 3 arguments. The & is not the problem.
For future reference: when James McNellis asked you for "the" declaration of your function, he meant the declaration which is visible in the translation unit making the call. In your pastebin code, the definition is not visible in that translation unit, and the compiler cannot reach in to a completely different .cpp file and realise that the function is supposed to have parameter defaults. In C++, default values are set up in the calling code, for reasons to do with how calling conventions work.
Having seen the code, the defaults should be given in the header and not in the implementation file.
When you are compiling "monkeycard.cpp", the compiler has only the information in the headers to work with. The compiler has no idea that blit_image has default arguments, and therefore cannot match the function to call.
I suspect that the function isn't declared properly.
There needs to be a prototype inside the class display scope such as:
void blit_image(SDL_Surface* img, int x=0, int y=0, SDL_Rect* clip=NULL);
When you pass an int & parameter (such as any named variable of type int) to an int argument, the value of the int & is copied into the new object of type int. The difference in types implies a conversion which entails a copy which implements pass-by-value. That is just how the C++ formalism works.
The error message you see is nothing else than a specific convention, which that particular compiler uses to generate error messages in cases like that. Apparently, when the compiler is unable to resolve a function call, it generates an error message where every Lvalue argument is reported as having reference type and every Rvalue argument is reported as having non-reference type. This makes some sense, since references in C++ exist specifically for implementing the concept of run-time-bound Lvalue. In fact, it might even turn out that this is exactly how the overload resolution is implemented internally in that compiler.
As for the reason for the error: the function you are trying to call does not exist (or exists, but has a non-matching set of parameters).
P.S. You said in the comments that the matching function actually does exist. That would mean that there's either a problem with the visibility of the function declaration, or a problem with the code you posted being "fake" (i.e. it is not the code you were actually compiling).
Primitives are not reference types in C++.
How do you know that it's the int& that's the cause for the error? The error simply says that the signature is in error. I'd recommend going back and checking the method signature to see what the root cause is.