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

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.

Related

Why can't void parameters exist in functions with more than one argument?

I "wanted" to use void as a placeholder (or overload disambiguator) or even as a shortcut to have functions with void return type called before entering a specific function like in the following example:
int f(void , int)
{
return 0;
}
void g()
{
}
int main()
{
f(g(), 1);
}
Now, this is not a real world problem (I know that I could just call g() before calling f()) but I was wondering why this is not doable, especially when I can e.g. explicitly return void types i.e. this is legal :
void h()
{
return g(); // this does a return void
}
EDIT
To explain the rationale behind asking this, I first thought that according to C legacy, void would be an incomplete type, so incomplete types cannot appear as function parameters, unlike pointers to incomplete types and hence the void* commonality. Now this would explain void as a "special case" signal for "no parameters" but after C++11 the Standard reads (3.9 [basic.types]) :
A type is a literal type if it is:
void; or
a scalar type; or
....
Being a literal type, I can't find elsewhere any rationale that would exclude void from candidate types for function parameters, neither the equivalent of old C's (prior to C11) "void is not a type". Now, my search may be lacking the required depth which is what I try to compensate for in this Q
A void parameter means the function has no parameters*. It wouldn't make much sense for a function with no parameters to have some parameters.
* This is inherited from C (and presumably kept for compatibility with that language), where a function declared without a parameter list is a function that can take any number of parameters of any type. In C++, such a function would have no parameters, removing the need to use void parameters.
The only real problem here is your function prototype:
int f(void , int)
You cannot give a void as a parameter. You can set it as a return value, meaning "this function returns nothing", or you can give it as only parameter, like this:
int f(void)
It would means "this function takes no parameter", but not as a parameter.
But to give a parameter of void type would mean you could declare a void variable and give it to your function, which would have no sense.
In your sample:
void h()
{
return g(); // this does a return void
}
This does not a return void. This does return nothing. This is as legal as:
void h()
{
return;
}
So here, you can clearly see void is just a meaning of nothing.
Try to use functions returning void as arguments, like you did:
f(g(), 1);
Should be avoided as much as possible.
I've wanted a void argument type in order to have a parameter that is zero-cost in release builds:
#ifdef NDEBUG
typedef DebugTracker* Foo;
#else
typedef void Foo;
#endif
int SomeFunction(Foo foo, ...) {
...
}
I can't find elsewhere any rationale that would exclude void from candidate types for function parameters
#juanchopanza has pointed out one thing, which is that C++ inherited C's f(void) meaning a function that takes no arguments. That being so, C++ still could have the feature but make void parameters act as if they had a default value of nothing...so having such a default if they were at the end of argument lists.
In language-design space, it's always easy to think of the case you have in mind and say "why not?". And if you look at something like libffi then it seems like prohibiting void for arguments makes the system less "pure". There's a count of bytes for each argument, how hard would it be to allow 0?
But there are questions to answer.
If void parameters are possible, then that posits the existence of void variables. How does a void variable act? What's its address? If you can't take the address of a void variable, how does that impact the compiler...the linker...what's going to happen with name-mangling, etc.
I don't know enough to tell you if the pretzel of the existing C and C++ standard can be untwisted in a way that void parameters do more good than harm. It would be an interesting study to take a compiler and some large body of code and think through the details. I upvoted the question as reasonable to ask, but also voted to close as primarily opinion-based, so... that's my 0.02.

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);

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.

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

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.

C++ - Constructor overloading - private and public

Can you tell me why the following code is giving me the following error - call of overloaded "C(int)" is ambiguous
I would think that since C(char x) is private, only the C(float) ctor is visible from outside and that should be called by converting int to float.
But that's not the case.
class C
{
C(char x)
{
}
public:
C(float t)
{
}
};
int main()
{
C p(0);
}
This is discussed in "Effective C++" by Scott Meyer. The reason this is ambiguous is that they wanted to ensure that merely changing the visibility of a member wouldn't change the meaning of already-existing code elsewhere.
Otherwise, suppose your C class was in a header somewhere. If you had a private C(int) member, the code you present would call C(float). If, for some reason, the C(int) member was made public, the old code would suddenly call that member, even though neither the old code, nor the function it called had changed.
EDIT: More reasons:
Even worse, suppose you had the following 2 functions:
C A::foo()
{
return C(1.0);
}
C B::bar()
{
return C(1.0);
}
These two functions could call different functions depending on whether either foo or bar was declared as a friend of C, or whether A or B inherits from it. Having identical code call different functions is scary.
(That's probably not as well put as Scott Meyer's discussion, but that's the idea.)
0 is an int type. Because it can be implicitly cast to either a float or char equally, the call is ambiguous. Visibility is irrelevant for these purposes.
Either put 0.0, 0., or 0.0f, or get rid of the C(char) constructor entirely.
Edit: Relevant portion of the standard, section 13.3:
3) [...] But, once the candidate functions and argument lists have been identified, the selection of the best function is the same in all cases:
First, a subset of the candidate functions—those that have the proper number of arguments and meet certain other conditions—is selected to form a set of viable functions (13.3.2).
Then the best viable function is selected based on the implicit conversion sequences (13.3.3.1) needed to match each argument to the corresponding parameter of each viable function.
4) If a best viable function exists and is unique, overload resolution succeeds and produces it as the result. Otherwise overload resolution fails and the invocation is ill-formed. When overload resolution succeeds, and the best viable function is not accessible (clause 11) in the context in which it is used, the program is ill-formed.
Note that visibility is not part of the selection process.
I don't think that:
C p(0);
is being converted to:
C(float t)
you probably need to do:
C p(0.0f);