C++ 10 Statements
C++ 10 Statements
Article • 08/03/2021
C++ statements are the program elements that control how and in what order objects
are manipulated. This section includes:
Overview
Labeled Statements
Categories of Statements
Selection statements. These statements perform a test; they then execute one
section of code if the test evaluates to true (nonzero). They may execute
another section of code if the test evaluates to false.
See also
C++ Language Reference
Overview of C++ Statements
Article • 08/03/2021
labeled-statement
expression-statement
compound-statement
selection-statement
iteration-statement
jump-statement
declaration-statement
try-throw-catch
In most cases, the C++ statement syntax is identical to that of ANSI C89. The primary
difference between the two is that in C89, declarations are allowed only at the start of a
block; C++ adds the declaration-statement , which effectively removes this restriction.
This enables you to introduce variables at a point in the program where a precomputed
initialization value can be calculated.
Declaring variables inside blocks also allows you to exercise precise control over the
scope and lifetime of those variables.
break
case
catch
continue
default
do
else
__except
__finally
for
goto
if
__if_exists
__if_not_exists
__leave
return
switch
throw
__try
try
while
See also
Statements
Labeled statements
Article • 10/05/2021
Labels are used to transfer program control directly to the specified statement.
Syntax
labeled-statement :
identifier : statement
case constant-expression : statement
default : statement
Remarks
There are three types of labeled statements. All use a colon ( : ) to separate some type of
label from the statement. The case and default labels are specific to case statements.
C++
#include <iostream>
using namespace std;
void test_label(int x) {
if (x == 1){
goto label1;
}
goto label2;
label1:
cout << "in label1" << endl;
return;
label2:
cout << "in label2" << endl;
return;
}
int main() {
test_label(1); // in label1
test_label(2); // in label2
}
Labels and the goto statement
The appearance of an identifier label in the source program declares a label. Only a
goto statement can transfer control to an identifier label. The following code
fragment illustrates use of the goto statement and an identifier label:
A label can't appear by itself but must always be attached to a statement. If a label is
needed by itself, place a null statement after the label.
The label has function scope and can't be redeclared within the function. However, the
same name can be used as a label in different functions.
C++
// labels_with_goto.cpp
// compile with: /EHsc
#include <iostream>
int main() {
using namespace std;
goto Test2;
Test2:
cerr << "At Test2 label." << endl;
}
C++
case WM_CLOSE:
KillTimer( hWnd, TIMER1 );
DestroyWindow( hWnd );
if ( hWnd == hWndMain )
PostQuitMessage( 0 ); // Quit the application.
break;
default:
// This choice is taken for all messages not specifically
// covered by a case statement.
return DefWindowProc( hWnd, Message, wParam, lParam );
break;
}
See also
Overview of C++ statements
switch statement (C++)
Expression Statement
Article • 08/03/2021
Syntax
[expression ] ;
Remarks
All expressions in an expression statement are evaluated and all side effects are
completed before the next statement is executed. The most common expression
statements are assignments and function calls. Since the expression is optional, a
semicolon alone is considered an empty expression statement, referred to as the null
statement.
See also
Overview of C++ Statements
Null Statement
Article • 08/03/2021
The "null statement" is an expression statement with the expression missing. It is useful
when the syntax of the language calls for a statement but no expression evaluation. It
consists of a semicolon.
The following code fragment shows how to copy one string to another and incorporates
the null statement:
C++
// null_statement.cpp
char *myStrCpy( char *Dest, const char *Source )
{
char *DestStart = Dest;
return DestStart;
}
int main()
{
}
See also
Expression Statement
Compound Statements (Blocks)
Article • 08/03/2021
Syntax
{ [ statement-list ] }
Remarks
The following example uses a compound statement as the statement part of the if
statement (see The if Statement for details about the syntax):
C++
7 Note
See also
Overview of C++ Statements
Selection Statements (C++)
Article • 08/03/2021
The C++ selection statements, if and switch, provide a means to conditionally execute
sections of code.
The __if_exists and __if_not_exists statements allow you to conditionally include code
depending on the existence of a symbol.
See the individual topics for the syntax for each statement.
See also
Overview of C++ Statements
if-else statement (C++)
Article • 10/17/2023
following the optional else gets skipped. Otherwise, the following statement gets
skipped, and if there's an else then the statement following the else gets executed.
true
a non-null pointer,
any nonzero arithmetic value, or
a class type that defines an unambiguous conversion to an arithmetic, boolean, or
pointer type. (For information about conversions, see Standard Conversions.)
Syntax
init-statement :
expression-statement
simple-declaration
condition :
expression
attribute-specifier-seq opt decl-specifier-seq declarator brace-or-equal-
initializer
statement :
expression-statement
compound-statement
expression-statement :
expression opt ;
compound-statement :
{ statement-seq opt }
statement-seq :
statement
statement-seq statement
if-branch :
statement
else-branch :
statement
selection-statement :
if constexpr opt17 ( init-statement opt17 condition ) if-branch
if-else statements
In all forms of the if statement, condition , which can have any value except a
structure, is evaluated, including all side effects. Control passes from the if statement
to the next statement in the program unless the executed if-branch or else-branch
contains a break, continue, or goto.
The else clause of an if...else statement is associated with the closest previous if
statement in the same scope that doesn't have a corresponding else statement.
Example
This sample code shows several if statements in use, both with and without else :
C++
// if_else_statement.cpp
#include <iostream>
int main()
{
int x = 10;
if (x < 11)
{
cout << "x < 11 is true!\n"; // executed
}
else
{
cout << "x < 11 is false!\n"; // not executed
}
// no else statement
bool flag = false;
if (flag == true)
{
x = 100; // not executed
}
Output:
Output
x < 11 is true!
25
Example
C++
#include <iostream>
#include <mutex>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
string s{ "if" };
if (auto keywords = { "if", "for", "while" }; any_of(keywords.begin(),
keywords.end(), [&s](const char* kw) { return s == kw; }))
{
cout << "Error! Token must not be a keyword\n";
}
}
Output:
Output
ten
x is 42
setting shared_flag to false
Error! Token must not be a keyword
if constexpr statements
Starting in C++17, you can use an if constexpr statement in function templates to
make compile-time branching decisions without having to resort to multiple function
overloads. Microsoft-specific: This form is available starting in Visual Studio 2017
version 15.3, and requires at least the /std:c++17 compiler option.
Example
This example shows how you can conditionally compile a template based on the type
sent to it:
C++
template<typename T>
auto Show(T t)
{
//if (std::is_pointer_v<T>) // Show(a) results in compiler error for
return *t. Show(b) results in compiler error for return t.
if constexpr (std::is_pointer_v<T>) // This statement goes away for
Show(a)
{
return *t;
}
else
{
return t;
}
}
int main()
{
int a = 42;
int* pB = &a;
The if constexpr statement is evaluated at compile time, and the compiler only
generates code for the if branch that matches the type of the argument sent to the
function template. If you comment out the if constexpr statement and uncomment the
if statement, the compiler generates code for both branches. That means you get an
error:
If you call ShowValue(a); you get an error on return *t because t isn't a pointer,
even though the if statement is false and the code is never executed.
If you call ShowValue(pB); you get an error on return t because t is a pointer,
even though the if statement is true and the code is never executed.
Using if constexpr solves this problem because only the statement that matches the
type of the argument sent to the function template is compiled.
Output:
Output
42
42
See also
Selection Statements
Keywords
switch Statement (C++)
__if_exists Statement
Article • 08/03/2021
The __if_exists statement tests whether the specified identifier exists. If the identifier
exists, the specified statement block is executed.
Syntax
__if_exists ( identifier ) {
statements
};
Parameters
identifier
The identifier whose existence you want to test.
statements
One or more statements to execute if identifier exists.
Remarks
U Caution
To achieve the most reliable results, use the __if_exists statement under the
following constraints.
Use the __if_exists statement only in the body of a function. Outside of the body
of a function, the __if_exists statement can test only fully defined types.
When you test for overloaded functions, you cannot test for a specific form of the
overload.
The complement to the __if_exists statement is the __if_not_exists statement.
Example
Notice that this example uses templates, which is not advised.
C++
// the__if_exists_statement.cpp
// compile with: /EHsc
#include <iostream>
template<typename T>
class X : public T {
public:
void Dump() {
std::cout << "In X<T>::Dump()" << std::endl;
__if_exists(T::Dump) {
T::Dump();
}
__if_not_exists(T::Dump) {
std::cout << "T::Dump does not exist" << std::endl;
}
}
};
class A {
public:
void Dump() {
std::cout << "In A::Dump()" << std::endl;
}
};
class B {};
class C {
public:
void f(int);
void f(double);
};
int main() {
X<A> x1;
X<B> x2;
x1.Dump();
x2.Dump();
__if_exists(::g_bFlag) {
std::cout << "g_bFlag = " << g_bFlag << std::endl;
}
__if_exists(C::f) {
std::cout << "C::f exists" << std::endl;
}
return 0;
}
Output
Output
In X<T>::Dump()
In A::Dump()
In X<T>::Dump()
T::Dump does not exist
g_bFlag = 1
C::f exists
See also
Selection Statements
Keywords
__if_not_exists Statement
__if_not_exists Statement
Article • 08/03/2021
The __if_not_exists statement tests whether the specified identifier exists. If the
identifier does not exist, the specified statement block is executed.
Syntax
__if_not_exists ( identifier ) {
statements
};
Parameters
identifier
The identifier whose existence you want to test.
statements
One or more statements to execute if identifier does not exist.
Remarks
U Caution
To achieve the most reliable results, use the __if_not_exists statement under the
following constraints.
Use the __if_not_exists statement only in the body of a function. Outside of the
body of a function, the __if_not_exists statement can test only fully defined
types.
When you test for overloaded functions, you cannot test for a specific form of the
overload.
Example
For an example about how to use __if_not_exists , see __if_exists Statement.
See also
Selection Statements
Keywords
__if_exists Statement
switch statement (C++)
Article • 01/25/2023
Allows selection among multiple sections of code, depending on the value of an integral
expression.
Syntax
selection-statement :
init-statement :
expression-statement
simple-declaration
condition :
expression
attribute-specifier-seq opt decl-specifier-seq declarator brace-or-equal-
initializer
labeled-statement :
Remarks
A switch statement causes control to transfer to one labeled-statement in its statement
body, depending on the value of condition .
The condition must have an integral type, or be a class type that has an unambiguous
conversion to integral type. Integral promotion takes place as described in Standard
conversions.
The switch statement body consists of a series of case labels and an optional default
label. A labeled-statement is one of these labels and the statements that follow. The
labeled statements aren't syntactic requirements, but the switch statement is
meaningless without them. No two constant-expression values in case statements may
evaluate to the same value. The default label may appear only once. The default
statement is often placed at the end, but it can appear anywhere in the switch
statement body. A case or default label can only appear inside a switch statement.
The constant-expression in each case label is converted to a constant value that's the
same type as condition . Then, it's compared with condition for equality. Control passes
to the first statement after the case constant-expression value that matches the value
of condition . The resulting behavior is shown in the following table.
Condition Action
Converted value matches that of the promoted Control is transferred to the statement
controlling expression. following that label.
None of the constants match the constants in the case Control is transferred to the default
labels; a default label is present. label.
None of the constants match the constants in the case Control is transferred to the statement
labels; no default label is present. after the switch statement.
If a matching expression is found, execution can continue through later case or default
labels. The break statement is used to stop execution and transfer control to the
statement after the switch statement. Without a break statement, every statement from
the matched case label to the end of the switch , including the default , is executed.
For example:
C++
// switch_statement1.cpp
#include <stdio.h>
int main() {
const char *buffer = "Any character stream";
int uppercase_A, lowercase_a, other;
char c;
uppercase_A = lowercase_a = other = 0;
Visual Studio 2017 and later (available in /std:c++17 mode and later): The
[[fallthrough]] attribute is specified in the C++17 standard. You can use it in a switch
statement. It's a hint to the compiler, or anyone who reads the code, that fall-through
behavior is intentional. The Microsoft C++ compiler currently doesn't warn on
fallthrough behavior, so this attribute has no effect on compiler behavior. In the
example, the attribute gets applied to an empty statement within the unterminated
labeled statement. In other words, the semicolon is necessary.
C++
int main()
{
int n = 5;
switch (n)
{
case 1:
a();
break;
case 2:
b();
d();
[[fallthrough]]; // I meant to do this!
case 3:
c();
break;
default:
d();
break;
}
return 0;
}
Visual Studio 2017 version 15.3 and later (available in /std:c++17 mode and later): A
switch statement may have an init-statement clause, which ends with a semicolon. It
introduces and initializes a variable whose scope is limited to the block of the switch
statement:
C++
An inner block of a switch statement can contain definitions with initializers as long as
they're reachable, that is, not bypassed by all possible execution paths. Names
introduced using these declarations have local scope. For example:
C++
// switch_statement2.cpp
// C2360 expected
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
switch( tolower( *argv[1] ) )
{
// Error. Unreachable declaration.
char szChEntered[] = "Character entered was: ";
case 'a' :
{
// Declaration of szChEntered OK. Local scope.
char szChEntered[] = "Character entered was: ";
cout << szChEntered << "a\n";
}
break;
case 'b' :
// Value of szChEntered undefined.
cout << szChEntered << "b\n";
break;
default:
// Value of szChEntered undefined.
cout << szChEntered << "neither a nor b\n";
break;
}
}
A switch statement can be nested. When nested, the case or default labels associate
with the closest switch statement that encloses them.
Microsoft-specific behavior
Microsoft C++ doesn't limit the number of case values in a switch statement. The
number is limited only by the available memory.
See also
Selection Statements
Keywords
Iteration Statements (C++)
Article • 08/03/2021
C++ provides four iteration statements — while, do, for, and range-based for. Each of
these iterates until its termination expression evaluates to zero (false), or until loop
termination is forced with a break statement. The following table summarizes these
statements and their actions; each is discussed in detail in the sections that follow.
Iteration Statements
do Bottom of loop No No
See also
Overview of C++ Statements
while Statement (C++)
Article • 08/03/2021
Syntax
while ( expression )
statement
Remarks
The test of expression takes place before each execution of the loop; therefore, a while
loop executes zero or more times. expression must be of an integral type, a pointer type,
or a class type with an unambiguous conversion to an integral or pointer type.
A while loop can also terminate when a break, goto, or return within the statement
body is executed. Use continue to terminate the current iteration without exiting the
while loop. continue passes control to the next iteration of the while loop.
The following code uses a while loop to trim trailing underscores from a string:
C++
// while_statement.cpp
#include <string.h>
#include <stdio.h>
char *trim( char *szSource )
{
char *pszEOS = 0;
return szSource;
}
int main()
{
char szbuf[] = "12345_____";
The termination condition is evaluated at the top of the loop. If there are no trailing
underscores, the loop never executes.
See also
Iteration Statements
Keywords
do-while Statement (C++)
for Statement (C++)
Range-based for Statement (C++)
do-while Statement (C++)
Article • 08/03/2021
Syntax
do
statement
while ( expression ) ;
Remarks
The test of the termination condition is made after each execution of the loop;
therefore, a do-while loop executes one or more times, depending on the value of the
termination expression. The do-while statement can also terminate when a break, goto,
or return statement is executed within the statement body.
The expression must have arithmetic or pointer type. Execution proceeds as follows:
Example
The following sample demonstrates the do-while statement:
C++
// do_while_statement.cpp
#include <stdio.h>
int main()
{
int i = 0;
do
{
printf_s("\n%d",i++);
} while (i < 3);
}
See also
Iteration Statements
Keywords
while Statement (C++)
for Statement (C++)
Range-based for Statement (C++)
for statement (C++)
Article • 04/22/2022
Executes a statement repeatedly until the condition becomes false. For information on
the range-based for statement, see Range-based for statement (C++). For information
on the C++/CLI for each statement, see for each, in.
Syntax
for ( init-expression ; cond-expression ; loop-expression )
statement
Remarks
Use the for statement to construct loops that must execute a specified number of
times.
The for statement consists of three optional parts, as shown in the following table.
init- Before any other element of the for Often used to initialize loop indices. It can
expression statement, init-expression is contain expressions or declarations.
executed only once. Control then
passes to cond-expression .
loop- At the end of each iteration of Normally used to increment loop indices.
expression statement . After loop-expression is
executed, cond-expression is
evaluated.
The following examples show different ways to use the for statement.
C++
#include <iostream>
using namespace std;
int main() {
// The counter variable can be declared in the init-expression.
for (int i = 0; i < 2; i++ ){
cout << i;
}
// Output: 01
// The counter variable can be declared outside the for loop.
int i;
for (i = 0; i < 2; i++){
cout << i;
}
// Output: 01
// These for loops are the equivalent of a while loop.
i = 0;
while (i < 2){
cout << i++;
}
// Output: 01
}
C++
#include <iostream>
using namespace std;
int main(){
int i, j;
for ( i = 5, j = 10 ; i + j < 20; i++, j++ ) {
cout << "i + j = " << (i + j) << '\n';
}
}
/* Output:
i + j = 15
i + j = 17
i + j = 19
*/
C++
#include <iostream>
using namespace std;
int main(){
for (int i = 10; i > 0; i--) {
cout << i << ' ';
}
// Output: 10 9 8 7 6 5 4 3 2 1
for (int i = 10; i < 20; i = i+2) {
cout << i << ' ';
}
}
// Output: 10 12 14 16 18
A for loop terminates when a break, return, or goto (to a labeled statement outside the
for loop) within statement is executed. A continue statement in a for loop terminates
If cond-expression is omitted, it's considered true , and the for loop won't terminate
without a break , return , or goto within statement .
Although the three fields of the for statement are normally used for initialization,
testing for termination, and incrementing, they're not restricted to these uses. For
example, the following code prints the numbers 0 through 4. In this case, statement is
the null statement:
C++
#include <iostream>
using namespace std;
int main()
{
int i;
for( i = 0; i < 5; cout << i << '\n', i++){
;
}
}
C++
By default, under /Ze, a variable declared in a for loop remains in scope until the for
loop's enclosing scope ends.
It's also possible to use the scoping differences of the for loop to redeclare variables
under /Ze as follows:
C++
// for_statement5.cpp
int main(){
int i = 0; // hidden by var with same name declared in for loop
for ( int i = 0 ; i < 3; i++ ) {}
This behavior more closely mimics the standard behavior of a variable declared in a for
loop, which requires variables declared in a for loop to go out of scope after the loop is
done. When a variable is declared in a for loop, the compiler internally promotes it to a
local variable in the for loop's enclosing scope. It's promoted even if there's already a
local variable with the same name.
See also
Iteration statements
Keywords
while statement (C++)
do-while statement (C++)
Range-based for statement (C++)
Range-based for Statement (C++)
Article • 08/03/2021
Syntax
for ( for-range-declaration : expression )
statement
Remarks
Use the range-based for statement to construct loops that must execute through a
range, which is defined as anything that you can iterate through—for example,
std::vector , or any other C++ Standard Library sequence whose range is defined by a
begin() and end() . The name that is declared in the for-range-declaration portion is
local to the for statement and cannot be re-declared in expression or statement . Note
that the auto keyword is preferred in the for-range-declaration portion of the
statement.
New in Visual Studio 2017: Range-based for loops no longer require that begin() and
end() return objects of the same type. This enables end() to return a sentinel object
such as used by ranges as defined in the Ranges-V3 proposal. For more information, see
Generalizing the Range-Based For Loop and the range-v3 library on GitHub .
This code shows how to use range-based for loops to iterate through an array and a
vector:
C++
// range-based-for.cpp
// compile by using: cl /EHsc /nologo /W4
#include <iostream>
#include <vector>
using namespace std;
int main()
{
// Basic 10-element integer array.
int x[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Output
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
end of integer array test
0.14159 1.14159 2.14159 3.14159 4.14159 5.14159 6.14159 7.14159 8.14159
9.14159
end of vector test
See also
auto
Iteration Statements
Keywords
while Statement (C++)
do-while Statement (C++)
for Statement (C++)
Jump Statements (C++)
Article • 08/03/2021
Syntax
break;
continue;
return [expression];
goto identifier;
Remarks
See the following topics for a description of the C++ jump statements.
break Statement
continue Statement
return Statement
goto Statement
See also
Overview of C++ Statements
break Statement (C++)
Article • 08/03/2021
The break statement ends execution of the nearest enclosing loop or conditional
statement in which it appears. Control passes to the statement that follows the end of
the statement, if any.
Syntax
break;
Remarks
The break statement is used with the conditional switch statement and with the do, for,
and while loop statements.
In a switch statement, the break statement causes the program to execute the next
statement outside the switch statement. Without a break statement, every statement
from the matched case label to the end of the switch statement, including the default
clause, is executed.
In loops, the break statement ends execution of the nearest enclosing do , for , or
while statement. Control passes to the statement that follows the ended statement, if
any.
Within nested statements, the break statement ends only the do , for , switch , or while
statement that immediately encloses it. You can use a return or goto statement to
transfer control from more deeply nested structures.
Example
The following code shows how to use the break statement in a for loop.
C++
#include <iostream>
using namespace std;
int main()
{
// An example of a standard for loop
for (int i = 1; i < 10; i++)
{
if (i == 4) {
break;
}
cout << i << '\n';
}
Output
In each case:
1
2
3
The following code shows how to use break in a while loop and a do loop.
C++
#include <iostream>
using namespace std;
int main() {
int i = 0;
i = 0;
do {
if (i == 4) {
break;
}
cout << i << '\n';
i++;
} while (i < 10);
}
Output
In each case:
0123
The following code shows how to use break in a switch statement. You must use break
in every case if you want to handle each case separately; if you do not use break , the
code execution falls through to the next case.
C++
#include <iostream>
using namespace std;
int main() {
Suit hand;
. . .
// Assume that some enum value is set for hand
// In this example, each case is handled separately
switch (hand)
{
case Diamonds:
cout << "got Diamonds \n";
break;
case Hearts:
cout << "got Hearts \n";
break;
case Clubs:
cout << "got Clubs \n";
break;
case Spades:
cout << "got Spades \n";
break;
default:
cout << "didn't get card \n";
}
// In this example, Diamonds and Hearts are handled one way, and
// Clubs, Spades, and the default value are handled another way
switch (hand)
{
case Diamonds:
case Hearts:
cout << "got a red card \n";
break;
case Clubs:
case Spades:
default:
cout << "didn't get a red card \n";
}
}
See also
Jump Statements
Keywords
continue Statement
continue Statement (C++)
Article • 08/03/2021
Forces transfer of control to the controlling expression of the smallest enclosing do, for,
or while loop.
Syntax
continue;
Remarks
Any remaining statements in the current iteration are not executed. The next iteration of
the loop is determined as follows:
The following example shows how the continue statement can be used to bypass
sections of code and begin the next iteration of a loop.
Example
C++
// continue_statement.cpp
#include <stdio.h>
int main()
{
int i = 0;
do
{
i++;
printf_s("before the continue\n");
continue;
printf("after the continue, should never print\n");
} while (i < 3);
Output
See also
Jump Statements
Keywords
return Statement (C++)
Article • 08/03/2021
Terminates the execution of a function and returns control to the calling function (or to
the operating system if you transfer control from the main function). Execution resumes
in the calling function at the point immediately following the call.
Syntax
return [expression];
Remarks
The expression clause, if present, is converted to the type specified in the function
declaration, as if an initialization were being performed. Conversion from the type of the
expression to the return type of the function can create temporary objects. For more
information about how and when temporaries are created, see Temporary Objects.
The value of the expression clause is returned to the calling function. If the expression is
omitted, the return value of the function is undefined. Constructors and destructors, and
functions of type void ,cannot specify an expression in the return statement. Functions
of all other types must specify an expression in the return statement.
When the flow of control exits the block enclosing the function definition, the result is
the same as it would be if a return statement without an expression had been executed.
This is invalid for functions that are declared as returning a value.
The following example uses an expression with a return statement to obtain the largest
of two integers.
Example
C++
// return_statement2.cpp
#include <stdio.h>
int max ( int a, int b )
{
return ( a > b ? a : b );
}
int main()
{
int nOne = 5;
int nTwo = 7;
See also
Jump Statements
Keywords
goto Statement (C++)
Article • 08/03/2021
The goto statement unconditionally transfers control to the statement labeled by the
specified identifier.
Syntax
goto identifier;
Remarks
The labeled statement designated by identifier must be in the current function. All
identifier names are members of an internal namespace and therefore do not
A goto statement is not allowed to transfer control to a location that skips over the
initialization of any variable that is in scope in that location. The following example
raises C2362:
C++
int goto_fn(bool b)
{
if (!b)
{
goto exit; // C2362
}
else
{ /*...*/ }
exit:
return error_code;
}
It is good programming style to use the break , continue , and return statements
instead of the goto statement whenever possible. However, because the break
statement exits from only one level of a loop, you might have to use a goto statement
to exit a deeply nested loop.
For more information about labels and the goto statement, see Labeled Statements.
Example
In this example, a goto statement transfers control to the point labeled stop when i
equals 3.
C++
// goto_statement.cpp
#include <stdio.h>
int main()
{
int i, j;
stop:
printf_s( "Jumped to stop. i = %d\n", i );
}
Output
See also
Jump Statements
Keywords
Transfers of Control
Article • 08/03/2021
You can use the goto statement or a case label in a switch statement to specify a
program that branches past an initializer. Such code is illegal unless the declaration that
contains the initializer is in a block enclosed by the block in which the jump statement
occurs.
The following example shows a loop that declares and initializes the objects total , ch ,
and i . There is also an erroneous goto statement that transfers control past an
initializer.
C++
// transfers_of_control.cpp
// compile with: /W1
// Read input until a nonnumeric character is entered.
int main()
{
char MyArray[5] = {'2','2','a','c'};
int i = 0;
while( 1 )
{
int total = 0;
char ch = MyArray[i++];
int i = ch - '0';
Label1:
total += i; // C4700: transfers past initialization of i.
} // i would be destroyed here if goto error were not present
else
// Break statement transfers control out of loop,
// destroying total and ch.
break;
}
}
In the preceding example, the goto statement tries to transfer control past the
initialization of i . However, if i were declared but not initialized, the transfer would be
legal.
The objects total and ch , declared in the block that serves as the statement of the
while statement, are destroyed when that block is exited using the break statement.
Namespaces (C++)
Article • 08/03/2021
A namespace is a declarative region that provides a scope to the identifiers (the names
of types, functions, variables, etc) inside it. Namespaces are used to organize code into
logical groups and to prevent name collisions that can occur especially when your code
base includes multiple libraries. All identifiers at namespace scope are visible to one
another without qualification. Identifiers outside the namespace can access the
members by using the fully qualified name for each identifier, for example
std::vector<std::string> vec; , or else by a using Declaration for a single identifier
( using std::string ), or a using Directive for all the identifiers in the namespace ( using
namespace std; ). Code in header files should always use the fully qualified namespace
name.
The following example shows a namespace declaration and three ways that code
outside the namespace can accesses their members.
C++
namespace ContosoData
{
class ObjectManager
{
public:
void DoSomething() {}
};
void Func(ObjectManager) {}
}
C++
ContosoData::ObjectManager mgr;
mgr.DoSomething();
ContosoData::Func(mgr);
C++
using ContosoData::ObjectManager;
ObjectManager mgr;
mgr.DoSomething();
Use a using directive to bring everything in the namespace into scope:
C++
ObjectManager mgr;
mgr.DoSomething();
Func(mgr);
using directives
The using directive allows all the names in a namespace to be used without the
namespace-name as an explicit qualifier. Use a using directive in an implementation file
(i.e. *.cpp) if you are using several different identifiers in a namespace; if you are just
using one or two identifiers, then consider a using declaration to only bring those
identifiers into scope and not all the identifiers in the namespace. If a local variable has
the same name as a namespace variable, the namespace variable is hidden. It is an error
to have a namespace variable with the same name as a global variable.
7 Note
A using directive can be placed at the top of a .cpp file (at file scope), or inside a
class or function definition.
In general, avoid putting using directives in header files (*.h) because any file that
includes that header will bring everything in the namespace into scope, which can
cause name hiding and name collision problems that are very difficult to debug.
Always use fully qualified names in a header file. If those names get too long, you
can use a namespace alias to shorten them. (See below.)
C++
//contosoData.h
#pragma once
namespace ContosoDataServer
{
void Foo();
int Bar();
}
Function implementations in contosodata.cpp should use the fully qualified name, even
if you place a using directive at the top of the file:
C++
#include "contosodata.h"
using namespace ContosoDataServer;
A namespace can be declared in multiple blocks in a single file, and in multiple files. The
compiler joins the parts together during preprocessing and the resulting namespace
contains all the members declared in all the parts. An example of this is the std
namespace which is declared in each of the header files in the standard library.
Members of a named namespace can be defined outside the namespace in which they
are declared by explicit qualification of the name being defined. However, the definition
must appear after the point of declaration in a namespace that encloses the
declaration's namespace. For example:
C++
// defining_namespace_members.cpp
// C2039 expected
namespace V {
void f();
}
void V::f() { } // ok
void V::g() { } // C2039, g() is not yet a member of V
namespace V {
void g();
}
This error can occur when namespace members are declared across multiple header
files, and you have not included those headers in the correct order.
Nested namespaces
Namespaces may be nested. An ordinary nested namespace has unqualified access to its
parent's members, but the parent members do not have unqualified access to the
nested namespace (unless it is declared as inline), as shown in the following example:
C++
namespace ContosoDataServer
{
void Foo();
namespace Details
{
int CountImpl;
void Ban() { return Foo(); }
}
int Bar(){...};
int Baz(int i) { return Details::CountImpl; }
}
C++
//Header.h
#include <string>
namespace Test
{
namespace old_ns
{
std::string Func() { return std::string("Hello from old"); }
}
#include "header.h"
#include <string>
#include <iostream>
int main()
{
using namespace Test;
using namespace std;
string s = Func();
std::cout << s << std::endl; // "Hello from new"
return 0;
}
The following example shows how you can declare a specialization in a parent of a
template that is declared in an inline namespace:
C++
namespace Parent
{
inline namespace new_ns
{
template <typename T>
struct C
{
T member;
};
}
template<>
class C<int> {};
}
You can use inline namespaces as a versioning mechanism to manage changes to the
public interface of a library. For example, you can create a single parent namespace, and
encapsulate each version of the interface in its own namespace nested inside the parent.
The namespace that holds the most recent or preferred version is qualified as inline, and
is therefore exposed as if it were a direct member of the parent namespace. Client code
that invokes the Parent::Class will automatically bind to the new code. Clients that prefer
to use the older version can still access it by using the fully qualified path to the nested
namespace that has that code.
The inline keyword must be applied to the first declaration of the namespace in a
compilation unit.
The following example shows two versions of an interface, each in a nested namespace.
The v_20 namespace has some modification from the v_10 interface and is marked as
inline. Client code that uses the new library and calls Contoso::Funcs::Add will invoke
the v_20 version. Code that attempts to call Contoso::Funcs::Divide will now get a
compile time error. If they really need that function, they can still access the v_10
version by explicitly calling Contoso::v_10::Funcs::Divide .
C++
namespace Contoso
{
namespace v_10
{
template <typename T>
class Funcs
{
public:
Funcs(void);
T Add(T a, T b);
T Subtract(T a, T b);
T Multiply(T a, T b);
T Divide(T a, T b);
};
}
Namespace aliases
Namespace names need to be unique, which means that often they should not be too
short. If the length of a name makes code difficult to read, or is tedious to type in a
header file where using directives can't be used, then you can make a namespace alias
which serves as an abbreviation for the actual name. For example:
C++
C++
namespace
{
int MyFunc(){}
}
This is called an unnamed or anonymous namespace and it is useful when you want to
make variable declarations invisible to code in other files (i.e. give them internal linkage)
without having to create a named namespace. All code in the same file can see the
identifiers in an unnamed namespace but the identifiers, along with the namespace
itself, are not visible outside that file—or more precisely outside the translation unit.
See also
Declarations and Definitions
Enumerations (C++)
Article • 07/01/2022
7 Note
This article covers the ISO Standard C++ Language enum type and the scoped (or
strongly-typed) enum class type which is introduced in C++11. For information
about the public enum class or private enum class types in C++/CLI and C++/CX,
see enum class (C++/CLI and C++/CX).
Syntax
enum-name :
identifier
enum-specifier :
enum-head { enumerator-list opt }
enum-head { enumerator-list , }
enum-head :
enum-head-name :
nested-name-specifier opt identifier
opaque-enum-declaration :
enum-key attribute-specifier-seq opt enum-head-name enum-base opt ;
enum-key :
enum
enum class
enum struct
enum-base :
: type-specifier-seq
enumerator-list :
enumerator-definition
enumerator-list , enumerator-definition
enumerator-definition :
enumerator
enumerator = constant-expression
enumerator :
identifier attribute-specifier-seq opt
Usage
C++
// unscoped enum:
// enum [identifier] [: type] {enum-list};
// scoped enum:
// enum [class|struct] [identifier] [: type] {enum-list};
Parameters
identifier
type
The underlying type of the enumerators; all enumerators have the same underlying type.
May be any integral type.
enum-list
By using this keyword in the declaration, you specify the enum is scoped, and an
identifier must be provided. You can also use the struct keyword in place of class ,
Enumerator scope
An enumeration provides context to describe a range of values that are represented as
named constants. These named constants are also called enumerators. In the original C
and C++ enum types, the unqualified enumerators are visible throughout the scope in
which the enum is declared. In scoped enums, the enumerator name must be qualified
by the enum type name. The following example demonstrates this basic difference
between the two kinds of enums:
C++
namespace CardGame_Scoped
{
enum class Suit { Diamonds, Hearts, Clubs, Spades };
namespace CardGame_NonScoped
{
enum Suit { Diamonds, Hearts, Clubs, Spades };
Every name in an enumeration is assigned an integral value that corresponds to its place
in the order of the values in the enumeration. By default, the first value is assigned 0, the
next one is assigned 1, and so on, but you can explicitly set the value of an enumerator,
as shown here:
C++
enum Suit { Diamonds = 1, Hearts, Clubs, Spades };
Every enumerator is treated as a constant and must have a unique name within the
scope where the enum is defined (for unscoped enums) or within the enum itself (for
scoped enums). The values given to the names don't have to be unique. For example,
consider this declaration of an unscoped enum Suit :
C++
The values of Diamonds , Hearts , Clubs , and Spades are 5, 6, 4, and 5, respectively.
Notice that 5 is used more than once; it's allowed even though it may not be intended.
These rules are the same for scoped enums.
Casting rules
Unscoped enum constants can be implicitly converted to int , but an int is never
implicitly convertible to an enum value. The following example shows what happens if
you try to assign hand a value that isn't a Suit :
C++
C++
Using implicit conversions in this way can lead to unintended side-effects. To help
eliminate programming errors associated with unscoped enums, scoped enum values
are strongly typed. Scoped enumerators must be qualified by the enum type name
(identifier) and can't be implicitly converted, as shown in the following example:
C++
namespace ScopedEnumConversions
{
enum class Suit { Diamonds, Hearts, Clubs, Spades };
void AttemptConversions()
{
Suit hand;
hand = Clubs; // error C2065: 'Clubs' : undeclared identifier
hand = Suit::Clubs; //Correct.
int account_num = 135692;
hand = account_num; // error C2440: '=' : cannot convert from 'int'
to 'Suit'
hand = static_cast<Suit>(account_num); // OK, but probably a bug!!!
Notice that the line hand = account_num; still causes the error that occurs with unscoped
enums, as shown earlier. It's allowed with an explicit cast. However, with scoped enums,
the attempted conversion in the next statement, account_num = Suit::Hearts; , is no
longer allowed without an explicit cast.
C++
The new type is an exact copy of the underlying type, and therefore has the same calling
convention, which means it can be used across ABIs without any performance penalty.
No cast is required when variables of the type are initialized by using direct-list
initialization. The following example shows how to initialize enums with no enumerators
in various contexts:
C++
struct X
{
E e{ 0 };
X() : e{ 0 } { }
};
E* p = new E{ 0 };
int main()
{
f(E{ 0 });
byte i{ 42 };
byte j = byte{ 42 };
See also
C Enumeration declarations
Keywords
union
Article • 04/04/2023
7 Note
In C++17 and later, the std::variant class is a type-safe alternative for a union.
A union is a user-defined type in which all members share the same memory location.
This definition means that at any given time, a union can contain no more than one
object from its list of members. It also means that no matter how many members a
union has, it always uses only enough memory to store the largest member.
A union can be useful for conserving memory when you have lots of objects and limited
memory. However, a union requires extra care to use correctly. You're responsible for
ensuring that you always access the same member you assigned. If any member types
have a nontrivial constructor, then you must write code to explicitly construct and
destroy that member. Before you use a union, consider whether the problem you're
trying to solve could be better expressed by using a base class and derived class types.
Syntax
union tag opt { member-list };
Parameters
tag
member-list
Members that the union can contain.
Declare a union
Begin the declaration of a union by using the union keyword, and enclose the member
list in curly braces:
C++
// declaring_a_union.cpp
union RecordType // Declare a simple union type
{
char ch;
int i;
long l;
float f;
double d;
int *int_ptr;
};
int main()
{
RecordType t;
t.i = 5; // t holds an int
t.f = 7.25; // t now holds a float
}
Use a union
In the previous example, any code that accesses the union needs to know which
member holds the data. The most common solution to this problem is called a
discriminated union. It encloses the union in a struct, and includes an enum member that
indicates the member type currently stored in the union. The following example shows
the basic pattern:
C++
#include <queue>
struct TempData
{
int StationId;
time_t time;
double current;
double max;
double min;
};
struct WindData
{
int StationId;
time_t time;
int speed;
short direction;
};
struct Input
{
WeatherDataType type;
union
{
TempData temp;
WindData wind;
};
};
Input second;
second.type = WeatherDataType::Wind;
second.wind = { 204, 1418859354, 14, 27 };
inputs.push(second);
}
}
return 0;
}
In the previous example, the union in the Input struct has no name, so it's called an
anonymous union. Its members can be accessed directly as if they're members of the
struct. For more information about how to use an anonymous union, see the
Anonymous union section.
The previous example shows a problem that you could also solve by using class types
that derive from a common base class. You could branch your code based on the
runtime type of each object in the container. Your code might be easier to maintain and
understand, but it might also be slower than using a union. Also, with a union, you can
store unrelated types. A union lets you dynamically change the type of the stored value
without changing the type of the union variable itself. For example, you could create a
heterogeneous array of MyUnionType , whose elements store different values of different
types.
It's easy to misuse the Input struct in the example. It's up to the user to use the
discriminator correctly to access the member that holds the data. You can protect
against misuse by making the union private and providing special access functions, as
shown in the next example.
C++
// for MyVariant
#include <crtdbg.h>
#include <new>
#include <utility>
struct A
{
A() = default;
A(int i, const string& str) : num(i), name(str) {}
int num;
string name;
//...
};
struct B
{
B() = default;
B(int i, const string& str) : num(i), name(str) {}
int num;
string name;
vector<int> vec;
// ...
};
MyVariant(Kind kind)
: kind_(kind)
{
switch (kind_)
{
case Kind::None:
break;
case Kind::A:
new (&a_) A();
break;
case Kind::B:
new (&b_) B();
break;
case Kind::Integer:
i_ = 0;
break;
default:
_ASSERT(false);
break;
}
}
~MyVariant()
{
switch (kind_)
{
case Kind::None:
break;
case Kind::A:
a_.~A();
break;
case Kind::B:
b_.~B();
break;
case Kind::Integer:
break;
default:
_ASSERT(false);
break;
}
kind_ = Kind::None;
}
MyVariant(MyVariant&& other)
: kind_(other.kind_)
{
switch (kind_)
{
case Kind::None:
break;
case Kind::A:
new (&a_) A(move(other.a_));
break;
case Kind::B:
new (&b_) B(move(other.b_));
break;
case Kind::Integer:
i_ = other.i_;
break;
default:
_ASSERT(false);
break;
}
other.kind_ = Kind::None;
}
MyVariant(const A& a)
: kind_(Kind::A), a_(a)
{
}
MyVariant(A&& a)
: kind_(Kind::A), a_(move(a))
{
}
MyVariant& operator=(A&& a)
{
if (kind_ != Kind::A)
{
this->~MyVariant();
new (this) MyVariant(move(a));
}
else
{
a_ = move(a);
}
return *this;
}
MyVariant(const B& b)
: kind_(Kind::B), b_(b)
{
}
MyVariant(B&& b)
: kind_(Kind::B), b_(move(b))
{
}
MyVariant& operator=(B&& b)
{
if (kind_ != Kind::B)
{
this->~MyVariant();
new (this) MyVariant(move(b));
}
else
{
b_ = move(b);
}
return *this;
}
MyVariant(int i)
: kind_(Kind::Integer), i_(i)
{
}
MyVariant& operator=(int i)
{
if (kind_ != Kind::Integer)
{
this->~MyVariant();
new (this) MyVariant(i);
}
else
{
i_ = i;
}
return *this;
}
B& GetB()
{
_ASSERT(kind_ == Kind::B);
return b_;
}
int& GetInteger()
{
_ASSERT(kind_ == Kind::Integer);
return i_;
}
private:
Kind kind_;
union
{
A a_;
B b_;
int i_;
};
};
#pragma warning (pop)
int main()
{
A a(1, "Hello from A");
B b(2, "Hello from B");
MyVariant mv_1 = a;
cout << "mv_1 = a: " << mv_1.GetA().name << endl;
mv_1 = b;
cout << "mv_1 = b: " << mv_1.GetB().name << endl;
mv_1 = A(3, "hello again from A");
cout << R"aaa(mv_1 = A(3, "hello again from A"): )aaa" <<
mv_1.GetA().name << endl;
mv_1 = 42;
cout << "mv_1 = 42: " << mv_1.GetInteger() << endl;
b.vec = { 10,20,30,40,50 };
mv_1 = move(b);
cout << "After move, mv_1 = b: vec.size = " << mv_1.GetB().vec.size() <<
endl;
A union can't store a reference. A union also doesn't support inheritance. That means
you can't use a union as a base class, or inherit from another class, or have virtual
functions.
Initialize a union
You can declare and initialize a union in the same statement by assigning an expression
enclosed in braces. The expression is evaluated and assigned to the first field of the
union.
C++
#include <iostream>
using namespace std;
union NumericType
{
short iValue;
long lValue;
double dValue;
};
int main()
{
union NumericType Values = { 10 }; // iValue = 10
cout << Values.iValue << endl;
Values.dValue = 3.1416;
cout << Values.dValue << endl;
}
/* Output:
10
3.141600
*/
Anonymous union
An anonymous union is one declared without a class-name or declarator-list .
union { member-list }
Names declared in an anonymous union are used directly, like nonmember variables. It
implies that the names declared in an anonymous union must be unique in the
surrounding scope.
See also
Classes and Structs
Keywords
class
struct
Functions (C++)
Article • 02/14/2023
A function is a block of code that performs some operation. A function can optionally
define input parameters that enable callers to pass arguments into the function. A
function can optionally return a value as output. Functions are useful for encapsulating
common operations in a single reusable block, ideally with a name that clearly describes
what the function does. The following function accepts two integers from a caller and
returns their sum; a and b are parameters of type int .
C++
The function can be invoked, or called, from any number of places in the program. The
values that are passed to the function are the arguments, whose types must be
compatible with the parameter types in the function definition.
C++
int main()
{
int i = sum(10, 32);
int j = sum(i, 66);
cout << "The value of j is" << j << endl; // 108
}
There's no practical limit to function length, but good design aims for functions that
perform a single well-defined task. Complex algorithms should be broken up into easy-
to-understand simpler functions whenever possible.
Functions that are defined at class scope are called member functions. In C++, unlike
other languages, a function can also be defined at namespace scope (including the
implicit global namespace). Such functions are called free functions or non-member
functions; they're used extensively in the Standard Library.
Functions may be overloaded, which means different versions of a function may share
the same name if they differ by the number and/or type of formal parameters. For more
information, see Function Overloading.
Parts of a function declaration
A minimal function declaration consists of the return type, function name, and
parameter list (which may be empty), along with optional keywords that provide more
instructions to the compiler. The following example is a function declaration:
C++
A function definition consists of a declaration, plus the body, which is all the code
between the curly braces:
C++
1. The return type, which specifies the type of the value that the function returns, or
void if no value is returned. In C++11, auto is a valid return type that instructs the
compiler to infer the type from the return statement. In C++14, decltype(auto) is
also allowed. For more information, see Type Deduction in Return Types below.
2. The function name, which must begin with a letter or underscore and can't contain
spaces. In general, leading underscores in the Standard Library function names
indicate private member functions, or non-member functions that aren't intended
for use by your code.
C++
C++
3. inline , which instructs the compiler to replace every call to the function with the
function code itself. inlining can help performance in scenarios where a function
executes quickly and is invoked repeatedly in a performance-critical section of
code.
C++
4. A noexcept expression, which specifies whether or not the function can throw an
exception. In the following example, the function doesn't throw an exception if the
is_pod expression evaluates to true .
C++
#include <type_traits>
5. (Member functions only) The cv-qualifiers, which specify whether the function is
const or volatile .
7. (member functions only) static applied to a member function means that the
function isn't associated with any object instances of the class.
Function definitions
A function definition consists of the declaration and the function body, enclosed in curly
braces, which contains variable declarations, statements and expressions. The following
example shows a complete function definition:
C++
Variables declared inside the body are called local variables or locals. They go out of
scope when the function exits; therefore, a function should never return a reference to a
local!
C++
MyClass& boom(int i, std::string s)
{
int value {i};
MyClass mc;
mc.Initialize(i,s);
return mc;
}
Declare a function as constexpr when the value it produces can possibly be determined
at compile time. A constexpr function generally executes faster than a regular function.
For more information, see constexpr.
Function Templates
A function template is similar to a class template; it generates concrete functions based
on the template arguments. In many cases, the template is able to infer the type
arguments and therefore it isn't necessary to explicitly specify them.
C++
By default, arguments are passed to the function by value, which means the function
receives a copy of the object being passed. For large objects, making a copy can be
expensive and isn't always necessary. To cause arguments to be passed by reference
(specifically lvalue reference), add a reference quantifier to the parameter:
C++
C++
C++ 11: To explicitly handle arguments that are passed by rvalue-reference or lvalue-
reference, use a double-ampersand on the parameter to indicate a universal reference:
C++
A function declared with the single keyword void in the parameter declaration list takes
no arguments, as long as the keyword void is the first and only member of the
argument declaration list. Arguments of type void elsewhere in the list produce errors.
For example:
C++
// OK same as GetTickCount()
long GetTickCount( void );
While it's illegal to specify a void argument except as outlined here, types derived from
type void (such as pointers to void and arrays of void ) can appear anywhere the
argument declaration list.
Default Arguments
The last parameter or parameters in a function signature may be assigned a default
argument, which means that the caller may leave out the argument when calling the
function unless they want to specify some other value.
C++
C++
When auto is used in conjunction with a trailing return type, it just serves as a
placeholder for whatever the decltype expression produces, and doesn't itself perform
type deduction.
In C++ a local variable may be declared as static. The variable is only visible inside the
function body, but a single copy of the variable exists for all instances of the function.
Local static objects are destroyed during termination specified by atexit . If a static
object wasn't constructed because the program's flow of control bypassed its
declaration, no attempt is made to destroy that object.
In this example, auto will be deduced as a non-const value copy of the sum of lhs and
rhs.
C++
The following example (based on code from N3493 ), shows decltype(auto) being
used to enable perfect forwarding of function arguments in a return type that isn't
known until the template is instantiated.
C++
1. Encapsulate the values in a named class or struct object. Requires the class or
struct definition to be visible to the caller:
C++
#include <string>
#include <iostream>
struct S
{
string name;
int num;
};
S g()
{
string t{ "hello" };
int u{ 42 };
return { t, u };
}
int main()
{
S s = g();
cout << s.name << " " << s.num << endl;
return 0;
}
C++
#include <tuple>
#include <string>
#include <iostream>
int main()
{
auto t = f();
cout << get<0>(t) << " " << get<1>(t) << " " << get<2>(t) << endl;
// --or--
int myval;
string myname;
double mydecimal;
tie(myval, myname, mydecimal) = f();
cout << myval << " " << myname << " " << mydecimal << endl;
return 0;
}
3. Visual Studio 2017 version 15.3 and later (available in /std:c++17 mode and later):
Use structured bindings. The advantage of structured bindings is that the variables
that store the return values are initialized at the same time they're declared, which
in some cases can be significantly more efficient. In the statement auto[x, y, z] =
f(); the brackets introduce and initialize names that are in scope for the entire
function block.
C++
#include <tuple>
#include <string>
#include <iostream>
S g()
{
string t{ "hello" };
int u{ 42 };
return { t, u };
}
int main()
{
auto[x, y, z] = f(); // init from tuple
cout << x << " " << y << " " << z << endl;
4. In addition to using the return value itself, you can "return" values by defining any
number of parameters to use pass-by-reference so that the function can modify or
initialize the values of objects that the caller provides. For more information, see
Reference-Type Function Arguments.
Function pointers
C++ supports function pointers in the same manner as the C language. However a more
type-safe alternative is usually to use a function object.
It's recommended that you use typedef to declare an alias for the function pointer type
if declaring a function that returns a function pointer type. For example
C++
If this isn't done, the proper syntax for the function declaration may be deduced from
the declarator syntax for the function pointer by replacing the identifier ( fp in the above
example) with the functions name and argument list, as follows:
C++
See also
Function Overloading
Functions with Variable Argument Lists
Explicitly Defaulted and Deleted Functions
Argument-Dependent Name (Koenig) Lookup on Functions
Default Arguments
Inline Functions
Functions with Variable Argument Lists
(C++)
Article • 08/03/2021
Function declarations in which the last member of is the ellipsis (...) can take a variable
number of arguments. In these cases, C++ provides type checking only for the explicitly
declared arguments. You can use variable argument lists when you need to make a
function so general that even the number and types of arguments can vary. The family
of functions is an example of functions that use variable argument
lists. printf argument-declaration-list
Microsoft Specific
Microsoft C++ allows the ellipsis to be specified as an argument if the ellipsis is the last
argument and the ellipsis is preceded by a comma. Therefore, the declaration int Func(
int i, ... ); is legal, but int Func( int i ... ); is not.
When arguments of type char are passed as variable arguments, they are converted to
type int . Similarly, when arguments of type float are passed as variable arguments,
they are converted to type double . Arguments of other types are subject to the usual
integral and floating-point promotions. See Standard Conversions for more information.
Functions that require variable lists are declared by using the ellipsis (...) in the argument
list. Use the types and macros that are described in the <stdarg.h> include file to access
arguments that are passed by a variable list. For more information about these macros,
see va_arg, va_copy, va_end, va_start. in the documentation for the C Run-Time Library.
The following example shows how the macros work together with the type (declared in
<stdarg.h>):
C++
// variable_argument_lists.cpp
#include <stdio.h>
#include <stdarg.h>
case 'f':
Printable.f = va_arg( vl, double );
printf_s( "%f\n", Printable.f );
break;
case 'c':
Printable.c = va_arg( vl, char );
printf_s( "%c\n", Printable.c );
break;
case 's':
Printable.s = va_arg( vl, char * );
printf_s( "%s\n", Printable.s );
break;
default:
break;
}
}
va_end( vl );
}
//Output:
// 32.400002
// a
// Test string
1. You must establish a list marker as a variable of type va_list before any variable
arguments are accessed. In the previous example, the marker is called vl .
2. The individual arguments are accessed by using the va_arg macro. You must tell
the va_arg macro the type of argument to retrieve so that it can transfer the
correct number of bytes from the stack. If you specify an incorrect type of a size
different from that supplied by the calling program to va_arg , the results are
unpredictable.
3. You should explicitly cast the result obtained by using the va_arg macro to the
type that you want.
C++ lets you specify more than one function of the same name in the same scope.
These functions are called overloaded functions, or overloads. Overloaded functions
enable you to supply different semantics for a function, depending on the types and
number of its arguments.
For example, consider a print function that takes a std::string argument. This
function might perform very different tasks than a function that takes an argument of
type double . Overloading keeps you from having to use names such as print_string or
print_double . At compile time, the compiler chooses which overload to use based on
the types and number of arguments passed in by the caller. If you call print(42.0) , then
the void print(double d) function is invoked. If you call print("hello world") , then the
void print(std::string) overload is invoked.
You can overload both member functions and free functions. The following table shows
which parts of a function declaration C++ uses to differentiate between groups of
functions with the same name in the same scope.
Overloading Considerations
Example
The following example illustrates how you can use function overloads:
C++
// function_overloading.cpp
// compile with: /EHsc
#include <iostream>
#include <math.h>
#include <string>
// Print a string.
int print(string s)
{
cout << s << endl;
return cout.good();
}
The preceding code shows overloads of the print function in file scope.
The default argument isn't considered part of the function type. Therefore, it's not used
in selecting overloaded functions. Two functions that differ only in their default
arguments are considered multiple definitions rather than overloaded functions.
Argument matching
The compiler selects which overloaded function to invoke based on the best match
among the function declarations in the current scope to the arguments supplied in the
function call. If a suitable function is found, that function is called. "Suitable" in this
context means either:
A set of "best matching functions" is built for each argument, and the selected function
is the intersection of all the sets. If the intersection contains more than one function, the
overloading is ambiguous and generates an error. The function that's eventually
selected is always a better match than every other function in the group for at least one
argument. If there's no clear winner, the function call generates a compiler error.
Consider the following declarations (the functions are marked Variant 1 , Variant 2 ,
and Variant 3 , for identification in the following discussion):
C++
C++
F1 = Add( F2, 23 );
Set 1: Candidate functions that have first Set 2: Candidate functions whose second
argument of type Fraction argument can be converted to type int
Variant 3
Functions in Set 2 are functions that have implicit conversions from the actual parameter
type to the formal parameter type. One of those functions has the smallest "cost" to
convert the actual parameter type to its corresponding formal parameter type.
The intersection of these two sets is Variant 1. An example of an ambiguous function call
is:
C++
F1 = Add( 3, 6 );
Set 1: Candidate Functions That Have First Set 2: Candidate Functions That Have Second
Argument of Type int Argument of Type int
Variant 2 ( int can be converted to long using Variant 1 ( int can be converted to long using a
a standard conversion) standard conversion)
Because the intersection of these two sets is empty, the compiler generates an error
message.
For argument matching, a function with n default arguments is treated as n+1 separate
functions, each with a different number of arguments.
The ellipsis ( ... ) acts as a wildcard; it matches any actual argument. It can lead to many
ambiguous sets, if you don't design your overloaded function sets with extreme care.
7 Note
For the same reason, function arguments of a type modified by const or volatile
aren't treated differently than the base type for the purposes of overloading.
However, the function overloading mechanism can distinguish between references that
are qualified by const and volatile and references to the base type. It makes code
such as the following possible:
C++
// argument_type_differences.cpp
// compile with: /EHsc /W3
// C4521 expected
#include <iostream>
int main() {
Over o1; // Calls default constructor.
Over o2( o1 ); // Calls Over( Over& ).
const Over o3; // Calls default constructor.
Over o4( o3 ); // Calls Over( const Over& ).
volatile Over o5; // Calls default constructor.
Over o6( o5 ); // Calls Over( volatile Over& ).
}
Output
Output
Pointers to const and volatile objects are also considered different from pointers to
the base type for the purposes of overloading.
The resultant sequence of conversions, if any, is called the best matching sequence. There
are several ways to convert an object of type int to type unsigned long using standard
conversions (described in Standard conversions):
Convert from int to long and then from long to unsigned long .
Although the first sequence achieves the desired goal, it isn't the best matching
sequence, because a shorter sequence exists.
The following table shows a group of conversions called trivial conversions. Trivial
conversions have a limited effect on which sequence the compiler chooses as the best
match. The effect of trivial conversions is described after the table.
Trivial conversions
type-name type-name&
type-name& type-name
type-name[] type-name*
type-name(argument-list) (*type-name)(argument-list)
1. Exact match. An exact match between the types with which the function is called
and the types declared in the function prototype is always the best match.
Sequences of trivial conversions are classified as exact matches. However,
sequences that don't make any of these conversions are considered better than
sequences that convert:
2. Match using promotions. Any sequence not classified as an exact match that
contains only integral promotions, conversions from float to double , and trivial
conversions is classified as a match using promotions. Although not as good a
match as any exact match, a match using promotions is better than a match using
standard conversions.
3. Match using standard conversions. Any sequence not classified as an exact match
or a match using promotions that contains only standard conversions and trivial
conversions is classified as a match using standard conversions. Within this
category, the following rules are applied:
This same rule applies to reference conversions. Conversion from type D& to type C& is
preferable to conversion from type D& to type B& , and so on.
This same rule applies to pointer-to-member conversions. Conversion from type T D::*
to type T C::* is preferable to conversion from type T D::* to type T B::* , and so on
(where T is the type of the member).
The preceding rule applies only along a given path of derivation. Consider the graph
shown in the following figure.
2. Match with an ellipsis. Any sequence that matches an ellipsis in the declaration is
classified as a match with an ellipsis. It's considered the weakest match.
C++
// argument_matching1.cpp
class UDC
{
public:
operator int()
{
return 0;
}
operator long();
};
void Print( int i )
{
};
UDC udc;
int main()
{
Print( udc );
}
The available user-defined conversions for class UDC are from type int and type long .
Therefore, the compiler considers conversions for the type of the object being matched:
UDC . A conversion to int exists, and it's selected.
C++
C++
// argument_matching2.cpp
// C2668 expected
class UDC1
{
public:
UDC1( int ); // User-defined conversion from int.
};
class UDC2
{
public:
UDC2( long ); // User-defined conversion from long.
};
int main()
{
Func( 1 );
}
Both versions of Func require a user-defined conversion to convert type int to the class
type argument. The possible conversions are:
Convert from type int to type long ; then convert to type UDC2 (a two-step
conversion).
Even though the second one requires both a standard conversion and the user-defined
conversion, the two conversions are still considered equal.
7 Note
Member functions that aren't static require the implied this pointer to match the
object type the function is being called through. Or, for overloaded operators, they
require the first argument to match the object the operator is applied to. For more
information about overloaded operators, see Overloaded operators.
Unlike other arguments in overloaded functions, the compiler introduces no temporary
objects and attempts no conversions when trying to match the this pointer argument.
When the -> member-selection operator is used to access a member function of class
class_name , the this pointer argument has a type of class_name * const . If the
members are declared as const or volatile , the types are const class_name * const
and volatile class_name * const , respectively.
The . member-selection operator works exactly the same way, except that an implicit &
(address-of) operator is prefixed to the object name. The following example shows how
this works:
C++
The left operand of the ->* and .* (pointer to member) operators are treated the same
way as the . and -> (member-selection) operators with respect to argument matching.
C++
#include <iostream>
#include <vector>
class C
{
public:
C() {/*expensive initialization*/}
vector<unsigned> get_data() &
{
cout << "lvalue\n";
return _data;
}
vector<unsigned> get_data() &&
{
cout << "rvalue\n";
return std::move(_data);
}
private:
vector<unsigned> _data;
};
int main()
{
C c;
auto v = c.get_data(); // get a copy. prints "lvalue".
auto v2 = C().get_data(); // get the original. prints "rvalue"
return 0;
}
Restrictions on overloading
Several restrictions govern an acceptable set of overloaded functions:
Any two functions in a set of overloaded functions must have different argument
lists.
Overloading functions that have argument lists of the same types, based on return
type alone, is an error.
Microsoft Specific
You can overload operator new based on the return type, specifically, based on the
memory-model modifier specified.
Member functions can't be overloaded solely because one is static and the other
isn't static .
typedef declarations don't define new types; they introduce synonyms for existing
types. They don't affect the overloading mechanism. Consider the following code:
C++
typedef char * PSTR;
The preceding two functions have identical argument lists. PSTR is a synonym for
type char * . In member scope, this code generates an error.
Enumerated types are distinct types and can be used to distinguish between
overloaded functions.
The types "array of" and "pointer to" are considered identical for the purposes of
distinguishing between overloaded functions, but only for one-dimensional arrays.
These overloaded functions conflict and generate an error message:
C++
For higher dimension arrays, the second and later dimensions are considered part
of the type. They're used in distinguishing between overloaded functions:
C++
Class scope is strictly observed. A function declared in a base class isn't in the same
scope as a function declared in a derived class. If a function in a derived class is declared
with the same name as a virtual function in the base class, the derived-class function
overrides the base-class function. For more information, see Virtual Functions.
If the base class function isn't declared as virtual , then the derived class function is
said to hide it. Both overriding and hiding are distinct from overloading.
Block scope is strictly observed. A function declared in file scope isn't in the same scope
as a function declared locally. If a locally declared function has the same name as a
function declared in file scope, the locally declared function hides the file-scoped
function instead of causing overloading. For example:
C++
// declaration_matching1.cpp
// compile with: /EHsc
#include <iostream>
int main()
{
// Declare func local to main.
extern void func( char *sz );
The preceding code shows two definitions from the function func . The definition that
takes an argument of type char * is local to main because of the extern statement.
Therefore, the definition that takes an argument of type int is hidden, and the first call
to func is in error.
For overloaded member functions, different versions of the function can be given
different access privileges. They're still considered to be in the scope of the enclosing
class and thus are overloaded functions. Consider the following code, in which the
member function Deposit is overloaded; one version is public, the other, private.
The intent of this sample is to provide an Account class in which a correct password is
required to perform deposits. It's done by using overloading.
The call to Deposit in Account::Deposit calls the private member function. This call is
correct because Account::Deposit is a member function, and has access to the private
members of the class.
C++
// declaration_matching2.cpp
class Account
{
public:
Account()
{
}
double Deposit( double dAmount, char *szPassword );
private:
double Deposit( double dAmount )
{
return 0.0;
}
int Validate( char *szPassword )
{
return 0;
}
};
int main()
{
// Allocate a new object of type Account.
Account *pAcct = new Account;
See also
Functions (C++)
Explicitly Defaulted and Deleted
Functions
Article • 11/15/2023
In C++11, defaulted and deleted functions give you explicit control over whether the
special member functions are automatically generated. Deleted functions also give you
simple language to prevent problematic type promotions from occurring in arguments
to functions of all types—special member functions, and normal member functions and
nonmember functions—which would otherwise cause an unwanted function call.
This is convenient for simple types, but complex types often define one or more of the
special member functions themselves, and this can prevent other special member
functions from being automatically generated. In practice:
7 Note
The consequences of these rules can also leak into object hierarchies. For example, if for
any reason a base class fails to have a default constructor that's callable from a deriving
class—that is, a public or protected constructor that takes no parameters—then a class
that derives from it can't automatically generate its own default constructor.
C++
struct noncopyable
{
noncopyable() {};
private:
noncopyable(const noncopyable&);
noncopyable& operator=(const noncopyable&);
};
Before C++11, this code snippet was the idiomatic form of noncopyable types. However,
it has several problems:
The copy constructor has to be declared privately to hide it, but because it's
declared at all, automatic generation of the default constructor is prevented. You
have to explicitly define the default constructor if you want one, even if it does
nothing.
Even if the explicitly defined default constructor does nothing, the compiler
considers it to be nontrivial. It's less efficient than an automatically generated
default constructor and prevents noncopyable from being a true POD type.
Even though the copy constructor and copy-assignment operator are hidden from
outside code, the member functions and friends of noncopyable can still see and
call them. If they're declared but not defined, calling them causes a linker error.
Although this is a commonly accepted idiom, the intent isn't clear unless you
understand all of the rules for automatic generation of the special member
functions.
C++
struct noncopyable
{
noncopyable() =default;
noncopyable(const noncopyable&) =delete;
noncopyable& operator=(const noncopyable&) =delete;
};
Notice how the problems with the pre-C++11 idiom are resolved:
Explicitly defaulted special member functions are still considered trivial, so there's
no performance penalty, and noncopyable isn't prevented from being a true POD
type.
The copy constructor and copy-assignment operator are public but deleted. It's a
compile-time error to define or call a deleted function.
The intent is clear to anyone who understands =default and =delete . You don't
have to understand the rules for automatic generation of special member
functions.
Similar idioms exist for making user-defined types that are nonmovable, that can only
be dynamically allocated, or that can't be dynamically allocated. Each of these idioms
have pre-C++11 implementations that suffer similar problems, and that are similarly
resolved in C++11 by implementing them in terms of defaulted and deleted special
member functions.
Explicitly defaulted functions
You can default any of the special member functions—to explicitly state that the special
member function uses the default implementation, to define the special member
function with a nonpublic access qualifier, or to reinstate a special member function
whose automatic generation was prevented by other circumstances.
C++
struct widget
{
widget()=default;
Notice that you can default a special member function outside the body of a class as
long as it's inlinable.
Deleted functions
You can delete special member functions and normal member functions and
nonmember functions to prevent them from being defined or called. Deleting of special
member functions provides a cleaner way of preventing the compiler from generating
special member functions that you don't want. The function must be deleted as it's
declared; it can't be deleted afterwards in the way that a function can be declared and
then later defaulted.
C++
struct widget
{
// deleted operator new prevents widget from being dynamically allocated.
void* operator new(std::size_t) = delete;
};
C++
by using an int argument wouldn't; in the int case, the argument will be promoted
from int to double and successfully call the double version of the function, even
though that might not be what you intend. To ensure that any call to this function by
using a non-double argument causes a compiler error, you can declare a template
version of the deleted function.
C++
The compiler can use argument-dependent name lookup to find the definition of an
unqualified function call. Argument-dependent name lookup is also called Koenig
lookup. The type of every argument in a function call is defined within a hierarchy of
namespaces, classes, structures, unions, or templates. When you specify an unqualified
postfix function call, the compiler searches for the function definition in the hierarchy
associated with each argument type.
Example
In the sample, the compiler notes that function f() takes an argument x . Argument x
is of type A::X , which is defined in namespace A . The compiler searches namespace A
and finds a definition for function f() that takes an argument of type A::X .
C++
// argument_dependent_name_koenig_lookup_on_functions.cpp
namespace A
{
struct X
{
};
void f(const X&)
{
}
}
int main()
{
// The compiler finds A::f() in namespace A, which is where
// the type of argument x is defined. The type of x is A::X.
A::X x;
f(x);
}
Default Arguments
Article • 08/03/2021
In many cases, functions have arguments that are used so infrequently that a default
value would suffice. To address this, the default-argument facility allows for specifying
only those arguments to a function that are meaningful in a given call. To illustrate this
concept, consider the example presented in Function Overloading.
C++
In many applications, a reasonable default can be supplied for prec , eliminating the
need for two functions:
C++
The implementation of the print function is changed slightly to reflect the fact that
only one such function exists for type double :
C++
// default_arguments.cpp
// compile with: /EHsc /c
#include <iostream>
#include <math.h>
using namespace std;
To invoke the new print function, use code such as the following:
C++
Default arguments are used only in function calls where trailing arguments are
omitted — they must be the last argument(s). Therefore, the following code is
illegal:
C++
C++
...
C++
The inline keyword suggests that the compiler substitute the code within the function
definition in place of each call to that function.
In theory, using inline functions can make your program faster because they eliminate
the overhead associated with function calls. Calling a function requires pushing the
return address on the stack, pushing arguments onto the stack, jumping to the function
body, and then executing a return instruction when the function finishes. This process is
eliminated by inlining the function. The compiler also has different opportunities to
optimize functions expanded inline versus those that aren't. A tradeoff of inline
functions is that the overall size of your program can increase.
Inline code substitution is done at the compiler's discretion. For example, the compiler
won't inline a function if its address is taken or if the compiler decides it's too large.
Example
In the following class declaration, the Account constructor is an inline function because
it is defined in the body of the class declaration. The member functions GetBalance ,
Deposit , and Withdraw are specified inline in their definitions. The inline keyword is
C++
// account.h
class Account
{
public:
Account(double initial_balance)
{
balance = initial_balance;
}
private:
double balance;
};
inline double Account::GetBalance() const
{
return balance;
}
7 Note
In the class declaration, the functions were declared without the inline keyword.
The inline keyword can be specified in the class declaration; the result is the same.
A given inline member function must be declared the same way in every compilation
unit. There must be exactly one definition of an inline function.
A class member function defaults to external linkage unless a definition for that function
contains the inline specifier. The preceding example shows that you don't have to
declare these functions explicitly with the inline specifier. Using inline in the function
definition suggests to the compiler that it be treated as an inline function. However, you
can't redeclare a function as inline after a call to that function.
The insertion, called inline expansion or inlining, occurs only if the compiler's own cost-
benefit analysis shows it's worthwhile. Inline expansion minimizes the function-call
overhead at the potential cost of larger code size.
The __forceinline keyword overrides the cost-benefit analysis and relies on the
judgment of the programmer instead. Exercise caution when using __forceinline .
Indiscriminate use of __forceinline can result in larger code with only marginal
performance gains or, in some cases, even performance losses (because of the increased
paging of a larger executable, for example).
The compiler treats the inline expansion options and keywords as suggestions. There's
no guarantee that functions will be inlined. You can't force the compiler to inline a
particular function, even with the __forceinline keyword. When you compile with /clr ,
the compiler won't inline a function if there are security attributes applied to the
function.
For compatibility with previous versions, _inline and _forceinline are synonyms for
__inline and __forceinline , respectively, unless compiler option /Za (Disable language
extensions) is specified.
The inline keyword tells the compiler that inline expansion is preferred. However, the
compiler can ignore it. Two cases where this behavior can happen are:
Recursive functions.
Functions that are referred to through a pointer elsewhere in the translation unit.
These reasons may interfere with inlining, as may others, as determined by the compiler.
Don't depend on the inline specifier to cause a function to be inlined.
Rather than expand an inline function defined in a header file, the compiler may create it
as a callable function in more than one translation unit. The compiler marks the
generated function for the linker to prevent one-definition-rule (ODR) violations.
As with normal functions, there's no defined order for argument evaluation in an inline
function. In fact, it could be different from the argument evaluation order when passed
using the normal function-call protocol.
Use the /Ob compiler optimization option to influence whether inline function
expansion actually occurs.
/LTCG does cross-module inlining whether it's requested in source code or not.
Example 1
C++
// inline_keyword1.cpp
// compile with: /c
inline int max(int a, int b)
{
return a < b ? b : a;
}
A class's member functions can be declared inline, either by using the inline keyword
or by placing the function definition within the class definition.
Example 2
C++
// inline_keyword2.cpp
// compile with: /EHsc /c
#include <iostream>
class MyClass
{
public:
void print() { std::cout << i; } // Implicitly inline
private:
int i;
};
Microsoft-specific
The function or its caller is compiled with /Ob0 (the default option for debug
builds).
The function and the caller use different types of exception handling (C++
exception handling in one, structured exception handling in the other).
The function has a variable argument list.
The function uses inline assembly, unless compiled with /Ox , /O1 , or /O2 .
The function is recursive and doesn't have #pragma inline_recursion(on) set. With
the pragma, recursive functions are inlined to a default depth of 16 calls. To reduce
the inlining depth, use inline_depth pragma.
The function is virtual and is called virtually. Direct calls to virtual functions can be
inlined.
The program takes the address of the function and the call is made via the pointer
to the function. Direct calls to functions that have had their address taken can be
inlined.
The function is also marked with the naked __declspec modifier.
If the compiler can't inline a function declared with __forceinline , it generates a level 1
warning, except when:
The function is compiled by using /Od or /Ob0. No inlining is expected in these
cases.
The function is defined externally, in an included library or another translation unit,
or is a virtual call target or indirect call target. The compiler can't identify non-
inlined code that it can't find in the current translation unit.
Recursive functions can be replaced with inline code to a depth specified by the
inline_depth pragma, up to a maximum of 16 calls. After that depth, recursive function
calls are treated as calls to an instance of the function. The depth to which recursive
functions are examined by the inline heuristic can't exceed 16. The inline_recursion
pragma controls the inline expansion of a function currently under expansion. See the
Inline-Function Expansion (/Ob) compiler option for related information.
C++
// when_to_use_inline_functions.cpp
// compile with: /c
class Point
{
public:
// Define "accessor" functions
// as reference types.
unsigned& x();
unsigned& y();
private:
unsigned _x;
unsigned _y;
};
inline unsigned& Point::x()
{
return _x;
}
Function calls (including parameter passing and placing the object's address on
the stack)
Preservation of caller's stack frame
New stack frame setup
Return-value communication
Restoring the old stack frame
Return
C++
#include <iostream>
#define mult1(a, b) a * b
#define mult2(a, b) (a) * (b)
#define mult3(a, b) ((a) * (b))
int main()
{
std::cout << (48 / mult1(2 + 2, 3 + 3)) << std::endl; // outputs 33
std::cout << (48 / mult2(2 + 2, 3 + 3)) << std::endl; // outputs 72
std::cout << (48 / mult3(2 + 2, 3 + 3)) << std::endl; // outputs 2
std::cout << (48 / multiply(2 + 2, 3 + 3)) << std::endl; // outputs 2
Output
33
72
2
2
4.4
4
Here are some of the differences between the macro and the inline function:
Macros are always expanded inline. However, an inline function is only inlined
when the compiler determines it is the optimal thing to do.
The macro may result in unexpected behavior, which can lead to subtle bugs. For
example, the expression mult1(2 + 2, 3 + 3) expands to 2 + 2 * 3 + 3 which
evaluates to 11, but the expected result is 24. A seemingly valid fix is to add
parentheses around both arguments of the function macro, resulting in #define
mult2(a, b) (a) * (b) , which will solve the issue at hand but can still cause
surprising behavior when part of a larger expression. That was demonstrated in the
preceding example, and the problem could be addressed by defining the macro as
such #define mult3(a, b) ((a) * (b)) .
An inline function is subject to semantic processing by the compiler, whereas the
preprocessor expands macros without that same benefit. Macros aren't type-safe,
whereas functions are.
Expressions passed as arguments to inline functions are evaluated once. In some
cases, expressions passed as arguments to macros can be evaluated more than
once. For example, consider the following:
C++
#include <iostream>
int main()
{
int c = 5;
std::cout << sqr(increment(c)) << std::endl; // outputs 30
std::cout << c << std::endl; // outputs 7
c = 5;
std::cout << square(increment(c)) << std::endl; // outputs 25
std::cout << c; // outputs 6
}
Output
30
7
25
6
second invocation of increment to return 6, hence the expression evaluates to 30. Any
expression that contains side effects may affect the result when used in a macro,
examine the fully expanded macro to check if the behavior is intended. Instead, if the
inline function square was used, the function increment would only be called once and
the correct result of 25 will be obtained.
See also
noinline
auto_inline
Operator overloading
Article • 02/17/2022
The operator keyword declares a function specifying what operator-symbol means when
applied to instances of a class. This gives the operator more than one meaning, or
"overloads" it. The compiler distinguishes between the different meanings of an
operator by examining the types of its operands.
Syntax
type operator operator-symbol ( parameter-list )
Remarks
You can redefine the function of most built-in operators globally or on a class-by-class
basis. Overloaded operators are implemented as functions.
Redefinable Operators
, Comma Binary
!= Inequality Binary
% Modulus Binary
() Function call —
* Multiplication Binary
+ Addition Binary
++ Increment 1 Unary
- Subtraction Binary
-- Decrement 1 Unary
/ Division Binary
= Assignment Binary
== Equality Binary
[] Array subscript —
^ Exclusive OR Binary
|| Logical OR Binary
delete Delete —
new New —
1 Two versions of the unary increment and decrement operators exist: preincrement and
postincrement.
See General Rules for Operator Overloading for more information. The constraints on
the various categories of overloaded operators are described in the following topics:
Unary Operators
Binary Operators
Assignment
Function Call
Subscripting
Class-Member Access
The operators shown in the following table cannot be overloaded. The table includes the
preprocessor symbols # and ##.
Nonredefinable Operators
Operator Name
. Member selection
.* Pointer-to-member selection
:: Scope resolution
?: Conditional
## Preprocessor concatenate
Although overloaded operators are usually called implicitly by the compiler when they
are encountered in code, they can be invoked explicitly the same way as any member or
nonmember function is called:
C++
Point pt;
pt.operator+( 3 ); // Call addition operator to add 3 to pt.
Example
The following example overloads the + operator to add two complex numbers and
returns the result.
C++
// operator_overloading.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
struct Complex {
Complex( double r, double i ) : re(r), im(i) {}
Complex operator+( Complex &other );
void Display( ) { cout << re << ", " << im << endl; }
private:
double re, im;
};
int main() {
Complex a = Complex( 1.2, 3.4 );
Complex b = Complex( 5.6, 7.8 );
Complex c = Complex( 0.0, 0.0 );
c = a + b;
c.Display();
}
Output
6.8, 11.2
In this section
General Rules for Operator Overloading
Binary Operators
Assignment
Function Call
Subscripting
Member Access
See also
C++ Built-in Operators, Precedence and Associativity
Keywords
General Rules for Operator Overloading
Article • 08/03/2021
The following rules constrain how overloaded operators are implemented. However,
they do not apply to the new and delete operators, which are covered separately.
You cannot redefine the meaning of operators when applied to built-in data types.
C++
// rules_for_operator_overloading.cpp
class Point
{
public:
Point operator<( Point & ); // Declare a member operator
// overload.
// Declare addition operators.
friend Point operator+( Point&, int );
friend Point operator+( int, Point& );
};
int main()
{
}
The preceding code sample declares the less-than operator as a member function;
however, the addition operators are declared as global functions that have friend
access. Note that more than one implementation can be provided for a given
operator. In the case of the preceding addition operator, the two implementations
are provided to facilitate commutativity. It is just as likely that operators that add a
Point to a Point , int to a Point , and so on, might be implemented.
If an operator can be used as either a unary or a binary operator (&, *, +, and -),
you can overload each use separately.
Note that the meaning of any of the operators can be changed completely. That
includes the meaning of the address-of (&), assignment (=), and function-call operators.
Also, identities that can be relied upon for built-in types can be changed using operator
overloading. For example, the following four statements are usually equivalent when
completely evaluated:
C++
var = var + 1;
var += 1;
var++;
++var;
This identity cannot be relied upon for class types that overload operators. Moreover,
some of the requirements implicit in the use of these operators for basic types are
relaxed for overloaded operators. For example, the addition/assignment operator, +=,
requires the left operand to be an l-value when applied to basic types; there is no such
requirement when the operator is overloaded.
7 Note
For consistency, it is often best to follow the model of the built-in types when
defining overloaded operators. If the semantics of an overloaded operator differ
significantly from its meaning in other contexts, it can be more confusing than
useful.
See also
Operator Overloading
Overloading unary operators
Article • 07/11/2022
Unary operators produce a result from a single operand. You can define overloads of a
standard set of unary operators to work on user-defined types.
! (logical NOT)
& (address-of)
~ (complement)
* (pointer dereference)
+ (unary plus)
- (unary negation)
Conversion operators
The following rules apply to all prefix unary operators. To declare a unary operator
function as a non-static member function, use this declaration form:
In this form, return-type is the return type, op is one of the operators listed in the
preceding table, and class-type is the class type of the argument on which to operate.
The postfix forms of ++ and -- take an extra int argument to distinguish them from
the prefix forms. For more information about the prefix and postfix forms of ++ and -- ,
see Increment and decrement operator overloading.
7 Note
There's no restriction on the return types of the unary operators. For example, it
makes sense for logical NOT ( ! ) to return a bool value, but this behavior isn't
enforced.
See also
Operator overloading
Increment and Decrement Operator
Overloading (C++)
Article • 08/10/2021
The increment and decrement operators fall into a special category because there are
two variants of each:
When you write overloaded operator functions, it can be useful to implement separate
versions for the prefix and postfix versions of these operators. To distinguish between
the two, the following rule is observed: The prefix form of the operator is declared
exactly the same way as any other unary operator; the postfix form accepts an extra
argument of type int .
7 Note
When specifying an overloaded operator for the postfix form of the increment or
decrement operator, the additional argument must be of type int ; specifying any
other type generates an error.
The following example shows how to define prefix and postfix increment and decrement
operators for the Point class:
C++
// increment_and_decrement1.cpp
class Point
{
public:
// Declare prefix and postfix increment operators.
Point& operator++(); // Prefix increment operator.
Point operator++(int); // Postfix increment operator.
int main()
{
}
The same operators can be defined in file scope (globally) using the following function
prototypes:
C++
C++
// increment_and_decrement2.cpp
class Int
{
public:
Int operator++( int n ); // Postfix increment operator
private:
int _i;
};
int main()
{
Int i;
i.operator++( 25 ); // Increment by 25.
}
There's no syntax for using the increment or decrement operators to pass these values
other than explicit invocation, as shown in the preceding code. A more straightforward
way to implement this functionality is to overload the addition/assignment operator
(+=).
See also
Operator Overloading
Binary Operators
Article • 02/17/2022
, Comma
!= Inequality
% Modulus
%= Modulus/assignment
* Multiplication
*= Multiplication/assignment
+ Addition
+= Addition/assignment
- Subtraction
-= Subtraction/assignment
/ Division
/= Division/assignment
= Assignment
== Equality
^ Exclusive OR
^= Exclusive OR/assignment
| Bitwise inclusive OR
|| Logical OR
To declare a binary operator function as a nonstatic member, you must declare it in the
form:
where ret-type is the return type, op is one of the operators listed in the preceding table,
and arg is an argument of any type.
To declare a binary operator function as a global function, you must declare it in the
form:
where ret-type and op are as described for member operator functions and arg1 and
arg2 are arguments. At least one of the arguments must be of class type.
7 Note
There is no restriction on the return types of the binary operators; however, most
user-defined binary operators return either a class type or a reference to a class
type.
See also
Operator Overloading
Assignment
Article • 08/03/2021
The assignment operator (=) is, strictly speaking, a binary operator. Its declaration is
identical to any other binary operator, with the following exceptions:
C++
class Point
{
public:
int _x, _y;
int main()
{
Point pt1, pt2;
pt1 = pt2;
}
The supplied argument is the right side of the expression. The operator returns the
object to preserve the behavior of the assignment operator, which returns the value of
the left side after the assignment is complete. This allows chaining of assignments, such
as:
C++
pt1 = pt2 = pt3;
The copy assignment operator is not to be confused with the copy constructor. The
latter is called during the construction of a new object from an existing one:
C++
7 Note
It is advisable to follow the rule of three that a class which defines a copy
assignment operator should also explicitly define copy constructor, destructor, and,
starting with C++11, move constructor and move assignment operator.
See also
Operator Overloading
Copy Constructors and Copy Assignment Operators (C++)
Function Call (C++)
Article • 08/03/2021
Syntax
primary-expression ( expression-list )
Remarks
In this context, primary-expression is the first operand, and expression-list , a possibly
empty list of arguments, is the second operand. The function-call operator is used for
operations that require a number of parameters. This works because expression-list is
a list instead of a single operand. The function-call operator must be a nonstatic
member function.
The function-call operator, when overloaded, does not modify how functions are called;
rather, it modifies how the operator is to be interpreted when applied to objects of a
given class type. For example, the following code would usually be meaningless:
C++
Point pt;
pt( 3, 2 );
C++
// function_call.cpp
class Point
{
public:
Point() { _x = _y = 0; }
Point &operator()( int dx, int dy )
{ _x += dx; _y += dy; return *this; }
private:
int _x, _y;
};
int main()
{
Point pt;
pt( 3, 2 );
}
Note that the function-call operator is applied to the name of an object, not the name
of a function.
You can also overload the function call operator using a pointer to a function (rather
than the function itself).
C++
typedef void(*ptf)();
void func()
{
}
struct S
{
operator ptf()
{
return func;
}
};
int main()
{
S s;
s();//operates as s.operator ptf()()
}
See also
Operator Overloading
Subscripting
Article • 08/03/2021
The subscript operator ([ ]), like the function-call operator, is considered a binary
operator. The subscript operator must be a nonstatic member function that takes a
single argument. This argument can be of any type and designates the desired array
subscript.
Example
The following example demonstrates how to create a vector of type int that
implements bounds checking:
C++
// subscripting.cpp
// compile with: /EHsc
#include <iostream>
// Construct an IntVector.
IntVector::IntVector( int cElements ) {
_iElements = new int[cElements];
_iUpperBound = cElements;
}
v[3] = v[9];
Output
Comments
When i reaches 10 in the preceding program, operator[] detects that an out-of-bounds
subscript is being used and issues an error message.
Note that the function operator[] returns a reference type. This causes it to be an l-
value, allowing you to use subscripted expressions on either side of assignment
operators.
See also
Operator Overloading
Member Access
Article • 08/03/2021
Class member access can be controlled by overloading the member access operator (-
>). This operator is considered a unary operator in this usage, and the overloaded
operator function must be a class member function. Therefore, the declaration for such
a function is:
Syntax
class-type *operator->()
Remarks
where class-type is the name of the class to which this operator belongs. The member
access operator function must be a nonstatic member function.
See also
Operator Overloading