Is calling of overload operator-> resolved at compile time? - c++

when I tried to compile the code:
(note: func and func2 is not typo)
struct S
{
void func2() {}
};
class O
{
public:
inline S* operator->() const;
private:
S* ses;
};
inline S* O::operator->() const
{
return ses;
}
int main()
{
O object;
object->func();
return 0;
}
there is a compile error reported:
D:\code>g++ operatorp.cpp -S -o operatorp.exe
operatorp.cpp: In function `int main()':
operatorp.cpp:27: error: 'struct S' has no member named 'func'
it seems that invoke the overloaded function of "operator->" is done during compile time? I'd added "-S" option for compile only.

Yes, it's treated like an ordinary function call, it's just called by an overloaded operator. The compiler checks everything is valid at compile time. C++ is not like dynamic languages where it waits until runtime to work out if p->mynonexistantfunction() is a valid name for a function or not, the compiler will reject the code at compile time if the function name does not exist.
In this case it looks like a typo, S has a function func2() but your code calls func().

In struct S you declared func2(), but in main,you try to call func().
try
int main()
{
O object;
object->func2();
return 0;
}

Yes, the compiler checks operators by it's result as any other function.
In this case if you had, for example,
S* foo() { ... }
(foo())->func();
the result would be the same.

In C++ language you cannot select a class member by name at run-time. Class member selection (by immediate member name) is always done at compile-time. There's no way around it.
If you want to implement member selection at run-time, the only thing you can use is operators .* and ->* (the former - non overloadable). However, these operators in their built-in form expect pointers-to-members as their right-hand operands. If you want to select something by name (as a string) you can overload ->* to make it take a different argument type, but in any case you'll have to implement the mapping from string to actual member manually. However, for member functions (as opposed to data members) this is usually pretty tricky.

object->func() is just syntactic sugar for object->operator->()->func() for user-defined types. Since O::operator->() yields an S*, this requires the existence of the method S::func() at compile time.

Related

Explicitly passing *this in 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.

What is the difference between &foo::function and foo::function?

I am using the gtkmm library on linux to draw a simple menu for my GUI.
In the below code the compiler complained about unable to resolve address
sigc::mem_fun(*this, AppWindow::hide)));
^
appwindow.cpp:15:41: note: could not resolve address from overloaded function
But when I insert the & it compiles fine
m_menu_app.items().push_back(MenuElem("Quit",
sigc::mem_fun(*this, &AppWindow::hide)));
What difference does it make here? Isn't the hide function just an address in the first place?
This is the exact definition of the function-to-pointer conversion, [conv.func]:
An lvalue of function type T can be converted to a prvalue of type
“pointer to T.” The result is a pointer to the function.55
55) This conversion never applies to non-static member functions because an lvalue that refers to a non-static member function
cannot be obtained.
Thus the decay that we see with normal, non-member functions1 doesn't apply and you need to explicitly take the address.
I.e.
void f();
struct A {
void f();
static void g();
};
auto a = f; // Ok: auto deduced as void(*)()
auto b = A::f; // Error: A::f not an lvalue, auto cannot be deduced
auto c = A::g; // Ok: auto deduced as void(*)()
1 Or static member functions.
For global (non-member) functions, the name of the function evaluates to the address of that function except when passed to the & operator, so you can (for example) assign to a pointer to a function either with or without the & equivalently:
int f() {}
int (*pf1)() = f;
int (*pf2)() = &f;
So, in this case there's really no difference between the two.
For member functions1, however, the rules are a bit different. In this case, the & is required; if you attempt to omit the &, the code simply won't compile (assuming a properly functioning compiler, anyway).
There's no particular reason this would have to be the case--it's just how Bjarne decided things should be. If he'd decided he wanted the name of a member function to evaluate to a pointer to a member (equivalent to how things work for non-member functions) he could have done that.
1. Other than static member functions, which mostly act like non-member functions.
When a function is a non-static member function of a class, then it is necessary to use the form &ClassName::functionName when a pointer to the member function is expected in an expression.
When a function is a static member function of a class, both ClassName::functionName and &ClassName;:functionName can be used when a pointer to a function is expected in an expression.
When a function is a global, i.e. non-member, function, both functionName and &functionName can be used when a pointer to a function is expected in an expression.

function pointers and return type conversions

Suppose I have a function that performs some side effect and then returns an answer:
int foo()
{
perform_some_side_effect();
return 42;
}
I want to bind foo to a function pointer, but I'm not interested in the answer, just the side effect:
void (*bar)() = foo;
However, this appears to be a type error:
error: invalid conversion from ‘int (*)()’ to ‘void (*)()’
What is the rationale behind that error? Why doesn't the type system allow me to ignore the answer?
On a side note, it works if I wrap the function pointer in a std::function:
std::function<void()> baz = foo;
How does std::function (apparently) manage to circumvent this restriction in the type system?
What is the rationale behind that error? Why doesn't the type system allow me to ignore the answer?
The reason is that the types are different, and the generated code at the place of call (through the function pointer) is different. Consider a calling convention where all arguments are written to the stack and space for the return value is also reserved in the stack. If the call goes through a void (*)() then no space will be reserved in the stack for the return value, but the function (unaware of how it is being called) will still write the 42 to the location where the caller should have reserved space.
How does std::function (apparently) manage to circumvent this restriction in the type system?
It does not. It creates a function object that wraps the call to the actual function. It will contain a member like:
void operator()() const {
foo();
}
Now when the compiler processes the call to foo it knows what it has to do to call a function that returns an int and it will do so according to the calling convention. Because the template does not return, it will just ignore the value --that was actually returned.
std::function need only be source compatible- that is, it can generate a new class which generates new caling code that ignores the result. The function pointer must be binary compatible and cannot do that job- void(*)() and int(*)() point to the exact same code.
You can think of std::function<> doing this for your particular case:
void __func_void()
{
foo();
}
It's actually a bit more complicated than that, but the point is that it generates template code together with type-erasure to not care about the specifics.
In addition to what others have been saying, the caller also need the return type to know what destructor it should invoke on the result (the return value may be a temporary).
Unfortunately it is not as easy as
auto (*bar)() = foo;
Although GCC and Clang accept this. I need to recheck the spec to see whether that's actually correct.
Update: The spec says
The auto type-specifier signifies that the type of a variable being declared shall be deduced from its initializer or that a function declarator shall include a trailing-return-type.
This can be misleading when read fast, but this is implemented by GCC and clang to only apply to the toplevel declarator. In our case, this is a pointer declarator. The declarator nested in it is a function declarator. So just substitute auto for void and then the compiler will deduce the type for you.
By the way, you can always make this work manually, but it takes some trickery to make it work
template<typename FunctionType>
struct Params;
template<typename ...Params>
struct Params<void(Params...)> {
template<typename T>
using Identity = T;
template<typename R>
static Identity<R(Params...)> *get(R f(Params...)) {
return f;
}
};
// now it's easy
auto bar = Params<void()>::get(foo);

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.

How does C++ pick which overloaded function to call?

Say I have three classes:
class X{};
class Y{};
class Both : public X, public Y {};
I mean to say I have two classes, and then a third class which extends both (multiple-inheritance).
Now say I have a function defined in another class:
void doIt(X *arg) { }
void doIt(Y *arg) { }
and I call this function with an instance of both:
doIt(new Both());
This causes a compile-time error, stating that the function call is ambiguous.
What are the cases, besides this one, where the C++ compiler decides the call is ambiguous and throws an error, if any? How does the compiler determine what these cases are?
Simple: if it's ambiguous, then the compiler gives you an error, forcing you to choose. In your snippet, you'll get a different error, because the type of new Both() is a pointer to Both, whereas both overloads of doIt() accept their parameters by value (i.e. they do not accept pointers). If you changed doIt() to take arguments of types X* and Y* respectively, the compiler would give you an error about the ambiguous function call.
If you want to explicitly call one or the other, you cast the arguments appropriately:
void doIt(X *arg) { }
void doIt(Y *arg) { }
Both *both = new Both;
doIt((X*)both); // calls doIt(X*)
doIt((Y*)both); // calls doIt(Y*)
delete both;
I get this error with gcc:
jeremy#jeremy-desktop:~/Desktop$ g++ -o test test.cpp
test.cpp: In function ‘int main(int, char**)’:
test.cpp:18: error: call of overloaded ‘doIt(Both&)’ is ambiguous
test.cpp:7: note: candidates are: void doIt(X)
test.cpp:11: note: void doIt(Y)
This is a perfect example of using boost::implicit_cast:
void doIt(X *arg) { }
void doIt(Y *arg) { }
doIt(boost::implicit_cast<X*>(new Both));
Unlike with other solutions (including static_cast), the cast will fail if no implicit conversion from Both* to X* is possible. This is done by a trick, best shown at a simple example:
X * implicit_conversion(X *b) { return b; }
That's what is boost::implicit_cast, just that it is a template which tells it the type of b.
The compiler does a depth-search, not a breadth-search for picking overloads.
The full answer is in Herb Sutter's exceptional C++, unfortunately I don't have the book in hand.
Edit: Got the book at hand now
It's called the depth first rule is called "The Interface Principle":
The Interface Principle For a class X,
all functions, including free
functions, that both (a) "mention"
X, and (b) are "supplied with" X
are logically part of X, because they
form part of the interface of X.
but there is a secondary rule called the "Koenig Lookup", that makes things harder.
Quote:"(simplified): if you supply a
function argument of class type (here
x, of type A::X), then to look up the
correct function name the compiler
considers matching names in the
namespace (here A) containing the
argument's type" -Herb Sutter,
Exceptional C++, p120
you need to explicitly cast your argument to either x or y
doIt(new Both());
so add...
(X *) or (Y *)
like...
doIt((X *)new Both());
AFAIK the C++ compiler will always pick the closest and most specific match that it can determine at compile time.
However, if your object is derived from both, I think that the compiler should give you an error or at least a very severe warning. Declaration order should not matter since this is about subtyping relations, and the first object is not "more of a subtype" than the other.