Expert C++
上QQ阅读APP看书,第一时间看更新

Special properties of main()

We concluded that main() is not actually the entry point of the program, though the standard states that it is the designated start. The compiler pays special attention to main(). It behaves like a regular C++ function, but besides being the first function to be called, it has other special properties. First of all, it is the only function that could omit the return statement:

int main() {
// works fine without a return statement
}

The returned value indicates the success of the execution. By returning 0, we aim to tell the control that main() ended successfully, so if the control reaches the end without encountering a corresponding return statement, it will consider the call successful and the effect is the same as return 0;

Another interesting property of the main() function is that its return type cannot be deduced automatically. It is not allowed to use the auto placeholder type specifier, which indicates that the return type will be deduced from function's return statement. Here's how it works for regular functions:

// C++11
auto foo() -> int {
std::cout << "foo in alternative function syntax" << std::endl;
return 0;
}

// C++14
auto foo() {

std::cout << "In C++14 syntax" << std::endl;
return 0;
}

By placing the auto specifier, we tell the compiler to automatically deduce the return type. With C++11, we also placed the type name after the arrow (->) although the second syntax is shorter. Consider the get_ratio() function, which returns the standard ratio as an integer:

auto get_ratio(bool minimum) {
if (minimum) {
return 12; // deduces return type int
}
return 18; // fine: get_ratio's return type is already deduced to int
}
To successfully compile C++ code containing new features specified in C++11, C++14, C++17, or C++20, you should use proper compiler flags. When compiling with g++, use the  --std  flag and specify the standard version. The recommended value is --std=c++2a.

The example compiles successfully, but look at what happens when we try the same trick with the main() function:

auto main() {
std::cout << get_ratio(true);
return 0;
}

The compiler will produce the following errors: 

  • cannot initialize return object of type 'auto' with an rvalue of type 'int'
  • 'main' must return 'int'.

Something strange is going on with the main() function. This is because the main() function allows omitting the return statement, but for the compiler, the return statement must exist to support automatic return type deduction. 

It's important to remember that if there are multiple return statements, they must all deduce to the same type. Let's suppose we need an updated version of the function, which returns an integer value (as shown in the previous example), and if specified, returns a more precise float value:

auto get_ratio(bool precise = false) {
if (precise) {
// returns a float value
return 4.114f;
}
return 4; // returns an int value
}

The preceding code won't compile successfully because there are two return statements with different deduced types.