Is main() overloaded in C++?
§3.6.1/2 (C++03) says
An implementation shall not predefine
themain
function. This function shall
not be overloaded. It shall have a
return type of type int, but otherwise
its type is implementation-defined.
All implementations shall allow both
of the following definitions of main:
int main() { /* ... */ }
int main(int argc, char* argv[]) { /* ... */ }
You can use either of them. Both are standard compliant.
Also, since char *argv[]
is equivalent to char **argv
, replacing char *argv[]
with char **argv
doesn't make any difference.
But both the versions cannot co-exist at the same time ! (use case can be like: while running the binary from command prompt, if you pass no argument then 1st version should be called else the 2nd version).
No. Both versions cannot co-exist at the same time. One program can have exactly one main
function. Which one, depends on your choice. If you want to process command-line argument, then you've to choose the second version, or else first version is enough. Also note that if you use second version, and don't pass any command line argument, then there is no harm in it. It will not cause any error. You just have to interpret argc
and argv
accordingly, and based on their value, you've to write the logic and the flow of your program.
Can we overload main() function in C++?
Yes it explicitly forbids that. Refer to 3.6.1p2
An implementation shall not predefine the main function. This function shall not be overloaded.
This way, the main function's name can stay unmangled. That is, the runtime library can call a symbol having a fixed name (e.g main
or _main
) to jump to the main function. The libraries' code will not need to depend on what parameter list the program's main
function has.
The implementation is also allowed to define additional valid parameter lists for the main
function (The POSIX spec specifies a char **env
parameter for the environment variables, for example). It would not be clear when an overload of main
is a "non-main function" or whether it's a "main function", thus an entry point. Presumably, you would want to get an error if you would declare more than one entry point, so such issues are important.
How to achieve function overloading in C?
There are few possibilities:
- printf style functions (type as an argument)
- opengl style functions (type in function name)
- c subset of c++ (if You can use a c++ compiler)
Does C support overloading?
No, C doesn't support any form of overloading (unless you count the fact that the built-in operators are overloaded already, to be a form of overloading).
printf
works using a feature called varargs. You make a call that looks like it might be overloaded:
printf("%d", 12); // int overload?
printf("%s", "hi"); // char* overload?
Actually it isn't. There is only one printf function, but the compiler uses a special calling convention to call it, where whatever arguments you provide are put in sequence on the stack[*]. printf (or vprintf) examines the format string and uses that to work out how to read those arguments back. This is why printf isn't type-safe:
char *format = "%d";
printf(format, "hi"); // undefined behaviour, no diagnostic required.
[*] the standard doesn't actually say they're passed on the stack, or mention a stack at all, but that's the natural implementation.
How does the main() method work in C?
Some of the features of the C language started out as hacks which just happened to work.
Multiple signatures for main, as well as variable-length argument lists, is one of those features.
Programmers noticed that they can pass extra arguments to a function, and nothing bad happens with their given compiler.
This is the case if the calling conventions are such that:
- The calling function cleans up the arguments.
- The leftmost arguments are closer to the top of the stack, or to the base of the stack frame, so that spurious arguments do not invalidate the addressing.
One set of calling conventions which obeys these rules is stack-based parameter passing whereby the caller pops the arguments, and they are pushed right to left:
;; pseudo-assembly-language
;; main(argc, argv, envp); call
push envp ;; rightmost argument
push argv ;;
push argc ;; leftmost argument ends up on top of stack
call main
pop ;; caller cleans up
pop
pop
In compilers where this type of calling convention is the case, nothing special need to be done to support the two kinds of main
, or even additional kinds. main
can be a function of no arguments, in which case it is oblivious to the items that were pushed onto the stack. If it's a function of two arguments, then it finds argc
and argv
as the two topmost stack items. If it's a platform-specific three-argument variant with an environment pointer (a common extension), that will work too: it will find that third argument as the third element from the top of the stack.
And so a fixed call works for all cases, allowing a single, fixed start-up module to be linked to the program. That module could be written in C, as a function resembling this:
/* I'm adding envp to show that even a popular platform-specific variant
can be handled. */
extern int main(int argc, char **argv, char **envp);
void __start(void)
{
/* This is the real startup function for the executable.
It performs a bunch of library initialization. */
/* ... */
/* And then: */
exit(main(argc_from_somewhere, argv_from_somewhere, envp_from_somewhere));
}
In other words, this start module just calls a three-argument main, always. If main takes no arguments, or only int, char **
, it happens to work fine, as well as if it takes no arguments, due to the calling conventions.
If you were to do this kind of thing in your program, it would be nonportable and considered undefined behavior by ISO C: declaring and calling a function in one manner, and defining it in another. But a compiler's startup trick does not have to be portable; it is not guided by the rules for portable programs.
But suppose that the calling conventions are such that it cannot work this way. In that case, the compiler has to treat main
specially. When it notices that it's compiling the main
function, it can generate code which is compatible with, say, a three argument call.
That is to say, you write this:
int main(void)
{
/* ... */
}
But when the compiler sees it, it essentially performs a code transformation so that the function which it compiles looks more like this:
int main(int __argc_ignore, char **__argv_ignore, char **__envp_ignore)
{
/* ... */
}
except that the names __argc_ignore
don't literally exist. No such names are introduced into your scope, and there won't be any warning about unused arguments.
The code transformation causes the compiler to emit code with the correct linkage which knows that it has to clean up three arguments.
Another implementation strategy is for the compiler or perhaps linker to custom-generate the __start
function (or whatever it is called), or at least select one from several pre-compiled alternatives. Information could be stored in the object file about which of the supported forms of main
is being used. The linker can look at this info, and select the correct version of the start-up module which contains a call to main
which is compatible with the program's definition. C implementations usually have only a small number of supported forms of main
so this approach is feasible.
Compilers for the C99 language always have to treat main
specially, to some extent, to support the hack that if the function terminates without a return
statement, the behavior is as if return 0
were executed. This, again, can be treated by a code transformation. The compiler notices that a function called main
is being compiled. Then it checks whether the end of the body is potentially reachable. If so, it inserts a return 0;
How c++ deduce the right overloaded function?
tl;dr:
std::pow
is a difficult example due to compilers providing more overloads & the c version being considered a builtin in gcc, so your problem is rather hard to demonstrate with it.- yes the call will actually be ambigous if the 5 functions you listed are the only ones competing in overload resolution.
pow()
is a rather tricky function to use for this, since there are a few gotchas:
In your godbolt example your actually calling
std::pow<int, int>
, i.e. a templated version of it. This is because you're using a rather new version of gcc that provides a templated overload ofstd::pow
for better performance.You can see this quite nicely in the generated assembly, line 6: (look at the end)
call __gnu_cxx::__promote_2<int, int, __gnu_cxx::__promote<int, std::__is_integer<int>::__value>::__type, __gnu_cxx::__promote<int, std::__is_integer<int>::__value>::__type>::__type std::pow<int, int>(int, int)
So for testing this you could do a few things:
- switch to an older version of gcc that doesn't have the templated overload yet, i.e. gcc 4.1.2:
godbolt example - or test it with user-specified functions, like the example from @mch:
godbolt example
- switch to an older version of gcc that doesn't have the templated overload yet, i.e. gcc 4.1.2:
Another problem is
<cmath>
itself - since it is the c++ equivalent of<math.h>
in c, gcc and clang do expose the cpow
function as well in it (withoutstd::
prefix) - this is why your code works even when removingusing namespace std;
.
Since c doesn't allow function overloads there's only one version withdouble
's. (the float version ispowf
, the long double onepowl
).
Additionally gcc considers them to be built-ins, so in this case it can just completely get rid of the pow call since it uses constants as arguments, even at-O0
.
This is what happened in the example provided by @ Aanchal Sharma: godbolt examplemov rax, QWORD PTR .LC0[rip]
movq xmm0, rax
// calculated value (64 as a double)
.LC0:
.long 0
.long 1078984704so gcc just calculated the result at compile-time and inlined it. (note there's no overload resolution problem at all here, since there's only one
pow
function in c - and the c++ functions aren't available becauseusing namespace std;
is not commented in)You can suppress this by passing
-fno-builtin
as a compiler argument, in which case gcc will dutifully emit the call todouble pow(double, double)
: godbolt examplemovsd xmm0, QWORD PTR .LC0[rip]
mov rax, QWORD PTR .LC1[rip]
movapd xmm1, xmm0
movq xmm0, rax
call pow
.LC0: // 3 as a double
.long 0
.long 1074266112
.LC1: // 4 as a double
.long 0
.long 1074790400
So to answer your questions:
1. Why does it work?
Because gcc actually provides a templated overload of std::pow
that is a perfect match in this case:
template<typename _Tp, typename _Up>
inline typename __gnu_cxx::__promote_2<_Tp, _Up>::__type pow(_Tp __x, _Up __y);
2. Assuming there would be no template overload, how would the compiler decide which overload to use?
The rules for determining the best overload are rather complicated, so i'll simplify them a bit (i'll completely ignore user-defined conversion functions, templated functions, variadic functions and a few other things not relevant to the question)
If you want to get a more complete picture of the actual rules you can take a look at the c++98 standard, section 13.3 Overload resolution
is what you're looking for.
Now to get into it:
First all functions are eliminated that don't have the right number of arguments. i.e. if there would be a
pow
function taking three arguments (and the third argument doesn't have a default value) - it would be directly eliminated.13.3.2 Viable functions
(1) First, to be a viable function, a candidate function shall have enough parameters to agree in number with the
arguments in the list.Secondly the parameters you provided actually need to be convertible to the parameters of the overload in question. If there's no way to convert between them the overload would be eliminated.
13.3.2 Viable functions
(3) Second, for F to be a viable function, there shall exist for each argument an implicit conversion sequence
(13.3.3.1) that converts that argument to the corresponding parameter of F.After that the compiler has a set of overloads left - the standard calls them Viable functions - that could be called in the given context. Now the compiler needs to actually decide which of those is the best one, i.e. the Best Viable Function, that should end up being called.
Now we get to the fun part - rating overloads based on how good of a fit they are. There are basically three primary categories a parameter conversion can have:
Exact Match > Promotion > Conversion
¹Exact Match
is the best one, this applies if the types match exactly or if it's just decaying an array to a pointer or function to a function pointer.Promotion
is slightly worse, this applies if you e.g. convert from one integral type to a bigger one or from a floating point type to a bigger one (but not between integral and floating points, and not to smaller types)Conversion
is the worst one and covers "downgrading" of int and float types (e.g.int -> char
,double -> float
) and conversion between integral and floating point types, e.g.int -> double
.
The compiler doesn't care what the exact conversion is, only in which of the three categories it falls. ²
So you could think of it that way: ³
- Each function gets a score based on the conversions needed for the arguments
Exact Match
is worth 0 pointsPromotion
is worth -1 pointsConversion
is worth -2 points- the function with the highest score gets called
- it there's a draw, the call is ambigous
So now to actually rank the
std::pow
overloads:// when called as pow(1, 1)
// 2x Conversion -> score -4
double pow (double base, double exponent);
// 2x Conversion -> score -4
float pow (float base, float exponent);
// 2x Conversion -> score -4
long double pow (long double base, long double exponent);
// 1x Conversion, 1x Exact -> score -2
double pow (double base, int exponent);
// 1x Conversion, 1x Exact -> score -2
long double pow (long double base, int exponent);So the first 3 are already off the table, because their score is worse.
This leaves the last 2, but those actually have the same score, so it's a draw!
-> You'll get an ambigous function call error
Footnotes:
¹ there are a few more rules for user-defined conversion functions, vararg functions and templates
(see 13.3.3.1 Implicit conversion sequences
)
² In reality there are quite a few more things to consider
(see 13.3.3.2 Ranking implicit conversion sequences
for a complete list)
³ Extremely simplified version. This is not how an actual compiler would do it.
Overloaded ++ operator only works from left side (C++)
Color& operator++(Color& source)
is for pre-incremant,
you need
Color operator++(Color& source, int)
for post increment.
Related Topics
How to Check If the Input Is a Valid Integer Without Any Other Chars
How Boost::Function and Boost::Bind Work
Allow for Range-Based for with Enum Classes
Common Reasons for Bugs in Release Version Not Present in Debug Mode
Use of the & Operator in C++ Function Signatures
Do Rvalue References Allow Dangling References
Error: Variable "Cannot Be Implicitly Captured Because No Default Capture Mode Has Been Specified"
Problem with Std::Map::Iterator After Calling Erase()
Simple Glob in C++ on Unix System
Writing Directly to Std::String Internal Buffers
How to Validate Input Using Scanf
Std::String Length() and Size() Member Functions
C++ Singleton VS. Global Static Object
C++ Lambdas How to Capture Variadic Parameter Pack from the Upper Scope
Using Std::Bind with Member Function, Use Object Pointer or Not for This Argument
C++ Efficiently Calculating a Running Median