0% found this document useful (0 votes)
10 views

C++ 10 Statements

Uploaded by

btech10253.19
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views

C++ 10 Statements

Uploaded by

btech10253.19
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 150

Statements (C++)

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

Expression statements. These statements evaluate an expression for its side


effects or for its return value.

Null statements. These statements can be provided where a statement is


required by the C++ syntax but where no action is to be taken.

Compound statements. These statements are groups of statements enclosed in


curly braces ({ }). They can be used wherever a single statement may be used.

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.

Iteration statements. These statements provide for repeated execution of a


block of code until a specified termination criterion is met.

Jump statements. These statements either transfer control immediately to


another location in the function or return control from the function.

Declaration statements. Declarations introduce a name into a program.

For information on exception handling statements see Exception Handling.

See also
C++ Language Reference
Overview of C++ Statements
Article • 08/03/2021

C++ statements are executed sequentially, except when an expression statement, a


selection statement, an iteration statement, or a jump statement specifically modifies
that sequence.

Statements may be of the following types:

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.

The articles on statements describe the following C++ keywords:

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

The scope of a label is the entire function in which it's declared.

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;

cout << "testing" << endl;

Test2:
cerr << "At Test2 label." << endl;
}

//Output: At Test2 label.

Labels in the case statement


Labels that appear after the case keyword can't also appear outside a switch
statement. (This restriction also applies to the default keyword.) The following code
fragment shows the correct use of case labels:

C++

// Sample Microsoft Windows message processing loop.


switch( msg )
{
case WM_TIMER: // Process timer event.
SetClassWord( hWnd, GCW_HICON, ahIcon[nIcon++] );
ShowWindow( hWnd, SW_SHOWNA );
nIcon %= 14;
Yield();
break;
case WM_PAINT:
memset( &ps, 0x00, sizeof(PAINTSTRUCT) );
hDC = BeginPaint( hWnd, &ps );
EndPaint( hWnd, &ps );
break;

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

Expression statements cause expressions to be evaluated. No transfer of control or


iteration takes place as a result of an expression statement.

The syntax for the expression statement is simply

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.

Null statements are commonly used as placeholders in iteration statements or as


statements on which to place labels at the end of compound statements or functions.

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;

// Assign value pointed to by Source to


// Dest until the end-of-string 0 is
// encountered.
while( *Dest++ = *Source++ )
; // Null statement.

return DestStart;
}

int main()
{
}

See also
Expression Statement
Compound Statements (Blocks)
Article • 08/03/2021

A compound statement consists of zero or more statements enclosed in curly braces ({


}). A compound statement can be used anywhere a statement is expected. Compound
statements are commonly called "blocks."

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++

if( Amount > 100 )


{
cout << "Amount was too large to handle\n";
Alert();
}
else
{
Balance -= Amount;
}

7 Note

Because a declaration is a statement, a declaration can be one of the statements in


the statement-list. As a result, names declared inside a compound statement, but
not explicitly declared as static, have local scope and (for objects) lifetime. See
Scope for details about treatment of names with local scope.

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

An if-else statement controls conditional branching. Statements in the if-branch are


executed only if the condition evaluates to a nonzero value (or true ). If the value of
condition is nonzero, the following statement gets executed, and the statement

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.

condition expressions that evaluate to nonzero are:

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 constexpr opt17 ( init-statement opt17 condition ) if-branch else else-branch

17 This optional element is available starting in C++17.

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>

using namespace std;

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
}

int *p = new int(25);


if (p)
{
cout << *p << "\n"; // outputs 25
}
else
{
cout << "p is null!\n"; // executed if memory allocation fails
}
}

Output:

Output

x < 11 is true!
25

if statement with an initializer


Starting in C++17, an if statement might also contain an init-statement expression
that declares and initializes a named variable. Use this form of the if-statement when the
variable is only needed within the scope of the if-statement. 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
C++

// Compile with /std:c++17

#include <iostream>
#include <mutex>
#include <map>
#include <string>
#include <algorithm>
using namespace std;

map<int, string> m{ {1, "one"}, {2, "two"}, {10,"ten"} };


mutex mx;
bool shared_flag = true; // guarded by mx
int getValue() { return 42; }

int main()
{

if (auto it = m.find(10); it != m.end())


{
cout << it->second << "\n";
}

if (int x = getValue(); x == 42)


{
cout << "x is 42\n";
}

if (lock_guard<mutex> lock(mx); shared_flag)


{
cout << "setting shared_flag to false\n";
shared_flag = false;
}

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++

// Compile with /std:c++17


#include <iostream>

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;

std::cout << Show(a) << "\n"; // prints "42"


std::cout << Show(pB) << "\n"; // prints "42"
}

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.

Apply the __if_exists statement to only simple types, not templates.

Apply the __if_exists statement to identifiers both inside or outside a class. Do


not apply the __if_exists statement to local variables.

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 {};

bool g_bFlag = true;

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.

Apply the __if_not_exists statement to only simple types, not templates.

Apply the __if_not_exists statement to identifiers both inside or outside a class.


Do not apply the __if_not_exists statement to local variables.

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.

The complement to the __if_not_exists statement is the __if_exists statement.

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 :

switch ( init-statement optC++17 condition ) statement

init-statement :

expression-statement
simple-declaration

condition :

expression
attribute-specifier-seq opt decl-specifier-seq declarator brace-or-equal-

initializer

labeled-statement :

case constant-expression : statement


default : 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.

switch statement behavior

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;

while ( c = *buffer++ ) // Walks buffer until NULL


{
switch ( c )
{
case 'A':
uppercase_A++;
break;
case 'a':
lowercase_a++;
break;
default:
other++;
}
}
printf_s( "\nUppercase A: %d\nLowercase a: %d\nTotal: %d\n",
uppercase_A, lowercase_a, (uppercase_A + lowercase_a + other) );
}

In the above example, uppercase_A is incremented if c is an uppercase 'A' . The break


statement after uppercase_A++ terminates execution of the switch statement body and
control passes to the while loop. Without the break statement, execution would "fall
through" to the next labeled statement, so that lowercase_a and other would also be
incremented. A similar purpose is served by the break statement for case 'a' . If c is a
lowercase 'a' , lowercase_a is incremented and the break statement terminates the
switch statement body. If c isn't an 'a' or 'A' , the default statement is executed.

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++

switch (Gadget gadget(args); auto s = gadget.get_status())


{
case status::good:
gadget.zip();
break;
case status::bad:
throw BadGadget();
};

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

Iteration statements cause statements (or compound statements) to be executed zero or


more times, subject to some loop-termination criteria. When these statements are
compound statements, they are executed in order, except when either the break
statement or the continue statement is encountered.

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

Statement Evaluated At Initialization Increment

while Top of loop No No

do Bottom of loop No No

for Top of loop Yes Yes

range-based for Top of loop Yes Yes

The statement part of an iteration statement cannot be a declaration. However, it can be


a compound statement containing a declaration.

See also
Overview of C++ Statements
while Statement (C++)
Article • 08/03/2021

Executes statement repeatedly until expression evaluates to zero.

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;

// Set pointer to character before terminating NULL


pszEOS = szSource + strlen( szSource ) - 1;

// iterate backwards until non '_' is found


while( (pszEOS >= szSource) && (*pszEOS == '_') )
*pszEOS-- = '\0';

return szSource;
}
int main()
{
char szbuf[] = "12345_____";

printf_s("\nBefore trim: %s", szbuf);


printf_s("\nAfter trim: %s\n", trim(szbuf));
}

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

Executes a statement repeatedly until the specified termination condition (the


expression) evaluates to zero.

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:

1. The statement body is executed.

2. Next, expression is evaluated. If expression is false, the do-while statement


terminates and control passes to the next statement in the program. If expression is
true (nonzero), the process is repeated, beginning with step 1.

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.

for loop elements

Syntax When executed Description


name

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 .

cond- Before execution of each iteration of An expression that evaluates to an integral


expression statement , including the first type or a class type that has an
iteration. statement is executed only unambiguous conversion to an integral
if cond-expression evaluates to true type. Normally used to test for loop-
(nonzero). termination criteria.

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
}

init-expression and loop-expression can contain multiple statements separated by

commas. For example:

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
*/

loop-expression can be incremented or decremented, or modified in other ways.

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

only the current iteration.

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++){
;
}
}

for loops and the C++ Standard


The C++ standard says that a variable declared in a for loop shall go out of scope after
the for loop ends. For example:

C++

for (int i = 0 ; i < 5 ; i++) {


// do something
}
// i is now out of scope under /Za or /Zc:forScope

By default, under /Ze, a variable declared in a for loop remains in scope until the for
loop's enclosing scope ends.

/Zc:forScope enables standard behavior of variables declared in for loops without


needing to specify /Za .

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++ ) {}

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

Executes statement repeatedly and sequentially for each element in expression .

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

// Range-based for loop to iterate through the array.


for( int y : x ) { // Access by value using a copy declared as a
specific type.
// Not preferred.
cout << y << " ";
}
cout << endl;

// The auto keyword causes type inference to be used. Preferred.

for( auto y : x ) { // Copy of 'x', almost always undesirable


cout << y << " ";
}
cout << endl;

for( auto &y : x ) { // Type inference by reference.


// Observes and/or modifies in-place. Preferred when modify is
needed.
cout << y << " ";
}
cout << endl;

for( const auto &y : x ) { // Type inference by const reference.


// Observes in-place. Preferred when no modify is needed.
cout << y << " ";
}
cout << endl;
cout << "end of integer array test" << endl;
cout << endl;

// Create a vector object that contains 10 elements.


vector<double> v;
for (int i = 0; i < 10; ++i) {
v.push_back(i + 0.14159);
}

// Range-based for loop to iterate through the vector, observing in-


place.
for( const auto &j : v ) {
cout << j << " ";
}
cout << endl;
cout << "end of vector test" << endl;
}

Here is the output:

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

A range-based for loop terminates when one of these in statement is executed: a


break, return, or goto to a labeled statement outside the range-based for loop. A
continue statement in a range-based for loop terminates only the current iteration.

Keep in mind these facts about range-based for :

Automatically recognizes arrays.

Recognizes containers that have .begin() and .end() .

Uses argument-dependent lookup begin() and end() for anything else.

See also
auto
Iteration Statements
Keywords
while Statement (C++)
do-while Statement (C++)
for Statement (C++)
Jump Statements (C++)
Article • 08/03/2021

A C++ jump statement performs an immediate local transfer of control.

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';
}

// An example of a range-based for loop


int nums []{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

for (int i : nums) {


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;

while (i < 10) {


if (i == 4) {
break;
}
cout << i << '\n';
i++;
}

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;

enum Suit{ Diamonds, Hearts, Clubs, Spades };

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:

In a do or while loop, the next iteration starts by reevaluating the controlling


expression of the do or while statement.

In a for loop (using the syntax for( <init-expr> ; <cond-expr> ; <loop-expr> ) ),


the <loop-expr> clause is executed. Then the <cond-expr> clause is reevaluated
and, depending on the result, the loop either ends or another iteration occurs.

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

printf_s("after the do loop\n");


}

Output

before the continue


before the continue
before the continue
after the do loop

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.

A function can have any number of return statements.

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;

printf_s("\n%d is bigger\n", max( nOne, nTwo ));


}

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

interfere with other identifiers.

A statement label is meaningful only to a goto statement; otherwise, statement labels


are ignored. Labels cannot be redeclared.

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
{ /*...*/ }

int error_code = 42;

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;

for ( i = 0; i < 10; i++ )


{
printf_s( "Outer loop executing. i = %d\n", i );
for ( j = 0; j < 2; j++ )
{
printf_s( " Inner loop executing. j = %d\n", j );
if ( i == 3 )
goto stop;
}
}

// This message does not print:


printf_s( "Loop exited. i = %d\n", i );

stop:
printf_s( "Jumped to stop. i = %d\n", i );
}

Output

Outer loop executing. i = 0


Inner loop executing. j = 0
Inner loop executing. j = 1
Outer loop executing. i = 1
Inner loop executing. j = 0
Inner loop executing. j = 1
Outer loop executing. i = 2
Inner loop executing. j = 0
Inner loop executing. j = 1
Outer loop executing. i = 3
Inner loop executing. j = 0
Jumped to stop. i = 3

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++];

if ( ch >= '0' && ch <= '9' )


{
goto Label1;

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) {}
}

Use the fully qualified name:

C++

ContosoData::ObjectManager mgr;
mgr.DoSomething();
ContosoData::Func(mgr);

Use a using declaration to bring one identifier into scope:

C++

using ContosoData::ObjectManager;
ObjectManager mgr;
mgr.DoSomething();
Use a using directive to bring everything in the namespace into scope:

C++

using namespace ContosoData;

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

Declaring namespaces and namespace


members
Typically, you declare a namespace in a header file. If your function implementations are
in a separate file, then qualify the function names, as in this example.

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;

void ContosoDataServer::Foo() // use fully-qualified name here


{
// no qualification needed for Bar()
Bar();
}

int ContosoDataServer::Bar(){return 0;}

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.

The global namespace


If an identifier is not declared in an explicit namespace, it is part of the implicit global
namespace. In general, try to avoid making declarations at global scope when possible,
except for the entry point main Function, which is required to be in the global
namespace. To explicitly qualify a global identifier, use the scope resolution operator
with no name, as in ::SomeFunction(x); . This will differentiate the identifier from
anything with the same name in any other namespace, and it will also help to make your
code easier for others to understand.

The std namespace


All C++ standard library types and functions are declared in the std namespace or
namespaces nested inside std .

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

Ordinary nested namespaces can be used to encapsulate internal implementation


details that are not part of the public interface of the parent namespace.
Inline namespaces (C++ 11)
In contrast to an ordinary nested namespace, members of an inline namespace are
treated as members of the parent namespace. This characteristic enables argument
dependent lookup on overloaded functions to work on functions that have overloads in
a parent and a nested inline namespace. It also enables you to declare a specialization in
a parent namespace for a template that is declared in the inline namespace. The
following example shows how external code binds to the inline namespace by default:

C++

//Header.h
#include <string>

namespace Test
{
namespace old_ns
{
std::string Func() { return std::string("Hello from old"); }
}

inline namespace new_ns


{
std::string Func() { return std::string("Hello from new"); }
}
}

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

inline namespace v_20


{
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);
std::vector<double> Log(double);
T Accumulate(std::vector<T> nums);
};
}
}

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++

namespace a_very_long_namespace_name { class Foo {}; }


namespace AVLNN = a_very_long_namespace_name;
void Bar(AVLNN::Foo foo){ }

anonymous or unnamed namespaces


You can create an explicit namespace but not give it a name:

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

An enumeration is a user-defined type that consists of a set of named integral constants


that are known as enumerators.

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-key attribute-specifier-seq opt enum-head-name opt enum-base opt

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

// Forward declaration of enumerations (C++11):


enum A : int; // non-scoped enum must have type specified
enum class B; // scoped enum defaults to int but ...
enum class C : short; // ... may have any integral underlying type

Parameters
identifier

The type name given to the enumeration.

type

The underlying type of the enumerators; all enumerators have the same underlying type.
May be any integral type.

enum-list

Comma-separated list of the enumerators in the enumeration. Every enumerator or


variable name in the scope must be unique. However, the values can be duplicated. In
an unscoped enum, the scope is the surrounding scope; in a scoped enum, the scope is
the enum-list itself. In a scoped enum, the list may be empty, which in effect defines a
new integral type.
class

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 ,

as they're semantically equivalent in this context.

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

void PlayCard(Suit suit)


{
if (suit == Suit::Clubs) // Enumerator must be qualified by enum
type
{ /*...*/}
}
}

namespace CardGame_NonScoped
{
enum Suit { Diamonds, Hearts, Clubs, Spades };

void PlayCard(Suit suit)


{
if (suit == Clubs) // Enumerator is visible without qualification
{ /*...*/
}
}
}

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

The enumerator Diamonds is assigned the value 1 . Subsequent enumerators, if they


aren't given an explicit value, receive the value of the previous enumerator plus one. In
the previous example, Hearts would have the value 2, Clubs would have 3, and so on.

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++

enum Suit { Diamonds = 5, Hearts, Clubs = 4, Spades };

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++

int account_num = 135692;


Suit hand;
hand = account_num; // error C2440: '=' : cannot convert from 'int' to
'Suit'

A cast is required to convert an int to a scoped or unscoped enumerator. However, you


can promote an unscoped enumerator to an integer value without a cast.

C++

int account_num = Hearts; //OK if Hearts is in a unscoped enum

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!!!

account_num = Suit::Hearts; // error C2440: '=' : cannot convert


from 'Suit' to 'int'
account_num = static_cast<int>(Suit::Hearts); // OK
}
}

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.

Enums with no enumerators


Visual Studio 2017 version 15.3 and later (Available with /std:c++17 and later): By
defining an enum (regular or scoped) with an explicit underlying type and no
enumerators, you can in effect introduce a new integral type that has no implicit
conversion to any other type. By using this type instead of its built-in underlying type,
you can eliminate the potential for subtle errors caused by inadvertent implicit
conversions.

C++

enum class byte : unsigned char { };

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++

enum class byte : unsigned char { };

enum class E : int { };


E e1{ 0 };
E e2 = E{ 0 };

struct X
{
E e{ 0 };
X() : e{ 0 } { }
};

E* p = new E{ 0 };

void f(E e) {};

int main()
{
f(E{ 0 });
byte i{ 42 };
byte j = byte{ 42 };

// unsigned char c = j; // C2440: 'initializing': cannot convert from


'byte' to 'unsigned char'
return 0;
}

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

The type name given to the union.

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>

using namespace std;

enum class WeatherDataType


{
Temperature, Wind
};

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

// Functions that are specific to data types


void Process_Temp(TempData t) {}
void Process_Wind(WindData w) {}

void Initialize(std::queue<Input>& inputs)


{
Input first;
first.type = WeatherDataType::Temperature;
first.temp = { 101, 1418855664, 91.8, 108.5, 67.2 };
inputs.push(first);

Input second;
second.type = WeatherDataType::Wind;
second.wind = { 204, 1418859354, 14, 27 };
inputs.push(second);
}

int main(int argc, char* argv[])


{
// Container for all the data records
queue<Input> inputs;
Initialize(inputs);
while (!inputs.empty())
{
Input const i = inputs.front();
switch (i.type)
{
case WeatherDataType::Temperature:
Process_Temp(i.temp);
break;
case WeatherDataType::Wind:
Process_Wind(i.wind);
break;
default:
break;
}
inputs.pop();

}
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.

Unrestricted union (C++11)


In C++03 and earlier, a union can contain nonstatic data members that have a class
type, as long as the type has no user provided constructors, destructors, or assignment
operators. In C++11, these restrictions are removed. If you include such a member in
your union, the compiler automatically marks any special member functions that aren't
user provided as deleted . If the union is an anonymous union inside a class or struct,
then any special member functions of the class or struct that aren't user provided are
marked as deleted . The following example shows how to handle this case. One of the
members of the union has a member that requires this special treatment:

C++

// for MyVariant
#include <crtdbg.h>
#include <new>
#include <utility>

// for sample objects and output


#include <string>
#include <vector>
#include <iostream>

using namespace std;

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;
// ...
};

enum class Kind { None, A, B, Integer };

#pragma warning (push)


#pragma warning(disable:4624)
class MyVariant
{
public:
MyVariant()
: kind_(Kind::None)
{
}

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(const MyVariant& other)


: kind_(other.kind_)
{
switch (kind_)
{
case Kind::None:
break;
case Kind::A:
new (&a_) A(other.a_);
break;
case Kind::B:
new (&b_) B(other.b_);
break;
case Kind::Integer:
i_ = other.i_;
break;
default:
_ASSERT(false);
break;
}
}

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& operator=(const MyVariant& other)


{
if (&other != this)
{
switch (other.kind_)
{
case Kind::None:
this->~MyVariant();
break;
case Kind::A:
*this = other.a_;
break;
case Kind::B:
*this = other.b_;
break;
case Kind::Integer:
*this = other.i_;
break;
default:
_ASSERT(false);
break;
}
}
return *this;
}

MyVariant& operator=(MyVariant&& other)


{
_ASSERT(this != &other);
switch (other.kind_)
{
case Kind::None:
this->~MyVariant();
break;
case Kind::A:
*this = move(other.a_);
break;
case Kind::B:
*this = move(other.b_);
break;
case Kind::Integer:
*this = other.i_;
break;
default:
_ASSERT(false);
break;
}
other.kind_ = Kind::None;
return *this;
}

MyVariant(const A& a)
: kind_(Kind::A), a_(a)
{
}

MyVariant(A&& a)
: kind_(Kind::A), a_(move(a))
{
}

MyVariant& operator=(const A& a)


{
if (kind_ != Kind::A)
{
this->~MyVariant();
new (this) MyVariant(a);
}
else
{
a_ = a;
}
return *this;
}

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=(const B& b)


{
if (kind_ != Kind::B)
{
this->~MyVariant();
new (this) MyVariant(b);
}
else
{
b_ = b;
}
return *this;
}

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

Kind GetKind() const


{
return kind_;
}
A& GetA()
{
_ASSERT(kind_ == Kind::A);
return a_;
}

const A& GetA() const


{
_ASSERT(kind_ == Kind::A);
return a_;
}

B& GetB()
{
_ASSERT(kind_ == Kind::B);
return b_;
}

const B& GetB() const


{
_ASSERT(kind_ == Kind::B);
return b_;
}

int& GetInteger()
{
_ASSERT(kind_ == Kind::Integer);
return i_;
}

const int& GetInteger() const


{
_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;

cout << endl << "Press a letter" << endl;


char c;
cin >> c;
}

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
*/

The NumericType union is arranged in memory (conceptually) as shown in the following


figure:

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.

An anonymous union is subject to these restrictions:

If declared in file or namespace scope, it must also be declared as static .


It can have only public members; having private and protected members in an
anonymous union generates errors.
It can't have member functions.

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++

int sum(int a, int b)


{
return a + b;
}

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++

int sum(int a, int b);

A function definition consists of a declaration, plus the body, which is all the code
between the curly braces:

C++

int sum(int a, int b)


{
return a + b;
}

A function declaration followed by a semicolon may appear in multiple places in a


program. It must appear prior to any calls to that function in each translation unit. The
function definition must appear only once in the program, according to the One
Definition Rule (ODR).

The required parts of a function declaration are:

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.

3. The parameter list, a brace delimited, comma-separated set of zero or more


parameters that specify the type and optionally a local name by which the values
may be accessed inside the function body.

Optional parts of a function declaration are:


1. constexpr , which indicates that the return value of the function is a constant value
can be computed at compile time.

C++

constexpr float exp(float x, int n)


{
return n == 0 ? 1 :
n % 2 == 0 ? exp(x * x, n / 2) :
exp(x * x, (n - 1) / 2) * x;
};

2. Its linkage specification, extern or static .

C++

//Declare printf with C linkage.


extern "C" int printf( const char *fmt, ... );

For more information, see Translation units and linkage.

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++

inline double Account::GetBalance()


{
return balance;
}

For more information, see Inline Functions.

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>

template <typename T>


T copy_object(T& obj) noexcept(std::is_pod<T>) {...}
For more information, see noexcept.

5. (Member functions only) The cv-qualifiers, which specify whether the function is
const or volatile .

6. (Member functions only) virtual , override , or final . virtual specifies that a


function can be overridden in a derived class. override means that a function in a
derived class is overriding a virtual function. final means a function can't be
overridden in any further derived class. For more information, see Virtual
Functions.

7. (member functions only) static applied to a member function means that the
function isn't associated with any object instances of the class.

8. (Non-static member functions only) The ref-qualifier, which specifies to the


compiler which overload of a function to choose when the implicit object
parameter ( *this ) is an rvalue reference vs. an lvalue reference. For more
information, see Function Overloading.

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++

int foo(int i, std::string s)


{
int value {i};
MyClass mc;
if(strcmp(s, "default") != 0)
{
value = mc.do_something(i);
}
return value;
}

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

const and constexpr functions


You can declare a member function as const to specify that the function isn't allowed to
change the values of any data members in the class. By declaring a member function as
const , you help the compiler to enforce const-correctness. If someone mistakenly tries
to modify the object by using a function declared as const , a compiler error is raised.
For more information, see const.

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++

template<typename Lhs, typename Rhs>


auto Add2(const Lhs& lhs, const Rhs& rhs)
{
return lhs + rhs;
}

auto a = Add2(3.13, 2.895); // a is a double


auto b = Add2(string{ "Hello" }, string{ " World" }); // b is a std::string

For more information, see Function Templates

Function parameters and arguments


A function has a comma-separated parameter list of zero or more types, each of which
has a name by which it can be accessed inside the function body. A function template
may specify more type or value parameters. The caller passes arguments, which are
concrete values whose types are compatible with the parameter list.

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++

void DoSomething(std::string& input){...}

When a function modifies an argument that is passed by reference, it modifies the


original object, not a local copy. To prevent a function from modifying such an
argument, qualify the parameter as const&:

C++

void DoSomething(const std::string& input){...}

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++

void DoSomething(const std::string&& input){...}

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++

int DoSomething(int num,


string str,
Allocator& alloc = defaultAllocator)
{ ... }

// OK both parameters are at end


int DoSomethingElse(int num,
string str = string{ "Working" },
Allocator& alloc = defaultAllocator)
{ ... }

// C2548: 'DoMore': missing default parameter for parameter 2


int DoMore(int num = 5, // Not a trailing parameter!
string str,
Allocator& = defaultAllocator)
{...}

For more information, see Default Arguments.

Function return types


A function may not return another function, or a built-in array; however it can return
pointers to these types, or a lambda, which produces a function object. Except for these
cases, a function may return a value of any type that is in scope, or it may return no
value, in which case the return type is void .

Trailing return types


An "ordinary" return type is located on the left side of the function signature. A trailing
return type is located on the rightmost side of the signature and is preceded by the ->
operator. Trailing return types are especially useful in function templates when the type
of the return value depends on template parameters.

C++

template<typename Lhs, typename Rhs>


auto Add(const Lhs& lhs, const Rhs& rhs) -> decltype(lhs + rhs)
{
return lhs + rhs;
}

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.

Function local variables


A variable that is declared inside a function body is called a local variable or simply a
local. Non-static locals are only visible inside the function body and, if they're declared
on the stack go out of scope when the function exits. When you construct a local
variable and return it by value, the compiler can usually perform the named return value
optimization to avoid unnecessary copy operations. If you return a local variable by
reference, the compiler will issue a warning because any attempt by the caller to use
that reference will occur after the local has been destroyed.

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.

Type deduction in return types (C++14)


In C++14, you can use auto to instruct the compiler to infer the return type from the
function body without having to provide a trailing return type. Note that auto always
deduces to a return-by-value. Use auto&& to instruct the compiler to deduce a
reference.

In this example, auto will be deduced as a non-const value copy of the sum of lhs and
rhs.

C++

template<typename Lhs, typename Rhs>


auto Add2(const Lhs& lhs, const Rhs& rhs)
{
return lhs + rhs; //returns a non-const object by value
}
Note that auto doesn't preserve the const-ness of the type it deduces. For forwarding
functions whose return value needs to preserve the const-ness or ref-ness of its
arguments, you can use the decltype(auto) keyword, which uses the decltype type
inference rules and preserves all the type information. decltype(auto) may be used as
an ordinary return value on the left side, or as a trailing return value.

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++

template<typename F, typename Tuple = tuple<T...>, int... I>


decltype(auto) apply_(F&& f, Tuple&& args, index_sequence<I...>)
{
return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(args))...);
}

template<typename F, typename Tuple = tuple<T...>,


typename Indices = make_index_sequence<tuple_size<Tuple>::value >>
decltype( auto)
apply(F&& f, Tuple&& args)
{
return apply_(std::forward<F>(f), std::forward<Tuple>(args), Indices());
}

Returning multiple values from a function


There are various ways to return more than one value from a function:

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>

using namespace std;

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

2. Return a std::tuple or std::pair object:

C++

#include <tuple>
#include <string>
#include <iostream>

using namespace std;

tuple<int, string, double> f()


{
int i{ 108 };
string s{ "Some text" };
double d{ .01 };
return { i,s,d };
}

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>

using namespace std;

tuple<int, string, double> f()


{
int i{ 108 };
string s{ "Some text" };
double d{ .01 };
return { i,s,d };
}
struct S
{
string name;
int num;
};

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;

auto[a, b] = g(); // init from POD struct


cout << a << " " << b << endl;
return 0;
}

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++

typedef int (*fp)(int);


fp myFunction(char* s); // function returning function pointer

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++

int (*myFunction(char* s))(int);

The preceding declaration is equivalent to the declaration using typedef earlier.

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

Functions with variable arguments


To access arguments after those declared, use the macros contained in the standard
include file <stdarg.h> as described below.

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.

END Microsoft Specific

Declaration of a function that takes a variable number of arguments requires at least


one placeholder argument, even if it is not used. If this placeholder argument is not
supplied, there is no way to access the remaining arguments.

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>

// Declaration, but not definition, of ShowVar.


void ShowVar( char *szTypes, ... );
int main() {
ShowVar( "fcsi", 32.4f, 'a', "Test string", 4 );
}

// ShowVar takes a format string of the form


// "ifcs", where each character specifies the
// type of the argument in that position.
//
// i = int
// f = float
// c = char
// s = string (char *)
//
// Following the format specification is a variable
// list of arguments. Each argument corresponds to
// a format character in the format string to which
// the szTypes parameter points
void ShowVar( char *szTypes, ... ) {
va_list vl;
int i;

// szTypes is the last argument specified; you must access


// all others using the variable-argument macros.
va_start( vl, szTypes );

// Step through the list.


for( i = 0; szTypes[i] != '\0'; ++i ) {
union Printable_t {
int i;
float f;
char c;
char *s;
} Printable;

switch( szTypes[i] ) { // Type to expect.


case 'i':
Printable.i = va_arg( vl, int );
printf_s( "%i\n", Printable.i );
break;

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

The previous example illustrates these important concepts:

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.

You must call the macro to terminate variable-argument processing. va_end


Function Overloading
Article • 02/14/2023

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

Function declaration element Used for overloading?

Function return type No

Number of arguments Yes

Type of arguments Yes

Presence or absence of ellipsis Yes

Use of typedef names No

Unspecified array bounds No

const or volatile Yes, when applied to entire function

Reference qualifiers ( & and && ) Yes

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>

// Prototype three print functions.


int print(std::string s); // Print a string.
int print(double dvalue); // Print a double.
int print(double dvalue, int prec); // Print a double with a
// given precision.
using namespace std;
int main(int argc, char *argv[])
{
const double d = 893094.2987;
if (argc < 2)
{
// These calls to print invoke print( char *s ).
print("This program requires one argument.");
print("The argument specifies the number of");
print("digits precision for the second number");
print("printed.");
exit(0);
}

// Invoke print( double dvalue ).


print(d);

// Invoke print( double dvalue, int prec ).


print(d, atoi(argv[1]));
}

// Print a string.
int print(string s)
{
cout << s << endl;
return cout.good();
}

// Print a double in default precision.


int print(double dvalue)
{
cout << dvalue << endl;
return cout.good();
}

// Print a double in specified precision.


// Positive numbers for precision indicate how many digits
// precision after the decimal point to show. Negative
// numbers for precision indicate where to round the number
// to the left of the decimal point.
int print(double dvalue, int prec)
{
// Use table-lookup for rounding/truncation.
static const double rgPow10[] = {
10E-7, 10E-6, 10E-5, 10E-4, 10E-3, 10E-2, 10E-1,
10E0, 10E1, 10E2, 10E3, 10E4, 10E5, 10E6 };
const int iPowZero = 6;

// If precision out of range, just print the number.


if (prec < -6 || prec > 7)
{
return print(dvalue);
}
// Scale, truncate, then rescale.
dvalue = floor(dvalue / rgPow10[iPowZero - prec]) *
rgPow10[iPowZero - prec];
cout << dvalue << 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.

Default arguments can't be supplied for overloaded operators.

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:

An exact match was found.

A trivial conversion was performed.

An integral promotion was performed.

A standard conversion to the desired argument type exists.

A user-defined conversion (either a conversion operator or a constructor) to the


desired argument type exists.

Arguments represented by an ellipsis were found.


The compiler creates a set of candidate functions for each argument. Candidate
functions are functions in which the actual argument in that position can be converted
to the type of the formal argument.

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++

Fraction &Add( Fraction &f, long l ); // Variant 1


Fraction &Add( long l, Fraction &f ); // Variant 2
Fraction &Add( Fraction &f, Fraction &f ); // Variant 3

Fraction F1, F2;

Consider the following statement:

C++

F1 = Add( F2, 23 );

The preceding statement builds two sets:

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 1 Variant 1 ( int can be converted to long using a


standard conversion)

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

The preceding function call builds the following sets:

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

Ambiguity of overloaded functions can't be determined until a function call is


encountered. At that point, the sets are built for each argument in the function call,
and you can determine whether an unambiguous overload exists. This means that
ambiguities can remain in your code until they're evoked by a particular function
call.

Argument Type Differences


Overloaded functions differentiate between argument types that take different
initializers. Therefore, an argument of a given type and a reference to that type are
considered the same for the purposes of overloading. They're considered the same
because they take the same initializers. For example, max( double, double ) is
considered the same as max( double &, double & ) . Declaring two such functions
causes an error.

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>

using namespace std;


class Over {
public:
Over() { cout << "Over default constructor\n"; }
Over( Over &o ) { cout << "Over&\n"; }
Over( const Over &co ) { cout << "const Over&\n"; }
Over( volatile Over &vo ) { cout << "volatile Over&\n"; }
};

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

Over default constructor


Over&
Over default constructor
const Over&
Over default constructor
volatile Over&

Pointers to const and volatile objects are also considered different from pointers to
the base type for the purposes of overloading.

Argument matching and conversions


When the compiler tries to match actual arguments against the arguments in function
declarations, it can supply standard or user-defined conversions to obtain the correct
type if no exact match can be found. The application of conversions is subject to these
rules:
Sequences of conversions that contain more than one user-defined conversion
aren't considered.

Sequences of conversions that can be shortened by removing intermediate


conversions aren't considered.

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 .

Convert from int 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

Argument type Converted type

type-name type-name&

type-name& type-name

type-name[] type-name*

type-name(argument-list) (*type-name)(argument-list)

type-name const type-name

type-name volatile type-name

type-name* const type-name*

type-name* volatile type-name*

The sequence in which conversions are attempted is as follows:

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:

From pointer, to pointer to const ( type-name* to const type-name* ).

From pointer, to pointer to volatile ( type-name* to volatile type-name* ).

From reference, to reference to const ( type-name& to const type-name& ).

From reference, to reference to volatile ( type-name& to volatile type& ).

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:

Conversion from a pointer to a derived class, to a pointer to a direct or


indirect base class is preferable to converting to void * or const void * .

Conversion from a pointer to a derived class, to a pointer to a base class


produces a better match the closer the base class is to a direct base class.
Suppose the class hierarchy is as shown in the following figure:

Graph showing preferred conversions.

Conversion from type D* to type C* is preferable to conversion from type D* to type


B* . Similarly, conversion from type D* to type B* is preferable to conversion from type
D* to type A* .

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.

Multiple-inheritance graph that shows preferred conversions.

Conversion from type C* to type B* is preferable to conversion from type C* to type


A* . The reason is that they are on the same path, and B* is closer. However, conversion
from type C* to type D* isn't preferable to conversion to type A* ; there's no preference
because the conversions follow different paths.

1. Match with user-defined conversions. This sequence can't be classified as an exact


match, a match using promotions, or a match using standard conversions. To be
classified as a match with user-defined conversions, the sequence must only
contain user-defined conversions, standard conversions, or trivial conversions. A
match with user-defined conversions is considered a better match than a match
with an ellipsis ( ... ) but not as good a match as a match with standard
conversions.

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.

User-defined conversions are applied if no built-in promotion or conversion exists.


These conversions are selected based on the type of the argument being matched.
Consider the following code:

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.

During the process of matching arguments, standard conversions can be applied to


both the argument and the result of a user-defined conversion. Therefore, the following
code works:

C++

void LogToFile( long l );


...
UDC udc;
LogToFile( udc );

In this example, the compiler invokes a user-defined conversion, operator long , to


convert udc to type long . If no user-defined conversion to type long was defined, the
compiler would first convert type UDC to type int using the user-defined operator int
conversion. Then it would apply the standard conversion from type int to type long to
match the argument in the declaration.

If any user-defined conversions are required to match an argument, the standard


conversions aren't used when evaluating the best match. Even if more than one
candidate function requires a user-defined conversion, the functions are considered
equal. For example:

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.
};

void Func( UDC1 );


void Func( UDC2 );

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 UDC1 (a user-defined conversion).

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

User-defined conversions are considered conversion by construction or conversion


by initialization. The compiler considers both methods equal when it determines
the best match.

Argument matching and the this pointer


Class member functions are treated differently, depending on whether they're declared
as static . static functions don't have an implicit argument that supplies the this
pointer, so they're considered to have one less argument than regular member
functions. Otherwise, they're declared identically.

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++

// Expression encountered in code


obj.name

// How the compiler treats it


(&obj)->name

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.

Reference-qualifiers on member functions


Reference qualifiers make it possible to overload a member function based on whether
the object pointed to by this is an rvalue or an lvalue. Use this feature to avoid
unnecessary copy operations in scenarios where you choose not to provide pointer
access to the data. For example, assume class C initializes some data in its constructor,
and returns a copy of that data in member function get_data() . If an object of type C is
an rvalue that's about to be destroyed, then the compiler chooses the get_data() &&
overload, which moves instead of copies the data.

C++

#include <iostream>
#include <vector>

using namespace std;

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.

END Microsoft Specific

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;

void Print( char *szToPrint );


void Print( PSTR szToPrint );

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++

void Print( char *szToPrint );


void Print( char szToPrint[] );

For higher dimension arrays, the second and later dimensions are considered part
of the type. They're used in distinguishing between overloaded functions:

C++

void Print( char szToPrint[] );


void Print( char szToPrint[][7] );
void Print( char szToPrint[][9][42] );

Overloading, overriding, and hiding


Any two function declarations of the same name in the same scope can refer to the
same function, or to two discrete overloaded functions. If the argument lists of the
declarations contain arguments of equivalent types (as described in the previous
section), the function declarations refer to the same function. Otherwise, they refer to
two different functions that are selected using overloading.

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>

using namespace std;


void func( int i )
{
cout << "Called file-scoped func : " << i << endl;
}

void func( char *sz )


{
cout << "Called locally declared func : " << sz << endl;
}

int main()
{
// Declare func local to main.
extern void func( char *sz );

func( 3 ); // C2664 Error. func( int ) is hidden.


func( "s" );
}

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;

// Deposit $57.22. Error: calls a private function.


// pAcct->Deposit( 57.22 );

// Deposit $57.22 and supply a password. OK: calls a


// public function.
pAcct->Deposit( 52.77, "pswd" );
}

double Account::Deposit( double dAmount, char *szPassword )


{
if ( Validate( szPassword ) )
return Deposit( dAmount );
else
return 0.0;
}

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.

Benefits of explicitly defaulted and deleted


functions
In C++, the compiler automatically generates the default constructor, copy constructor,
copy-assignment operator, and destructor for a type if it doesn't declare its own. These
functions are known as the special member functions, and they're what make simple
user-defined types in C++ behave like structures do in C. That is, you can create, copy,
and destroy them without extra coding effort. C++11 brings move semantics to the
language and adds the move constructor and move-assignment operator to the list of
special member functions that the compiler can automatically generate.

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:

If any constructor is explicitly declared, then no default constructor is automatically


generated.

If a virtual destructor is explicitly declared, then no default destructor is


automatically generated.

If a move constructor or move-assignment operator is explicitly declared, then:

No copy constructor is automatically generated.

No copy-assignment operator is automatically generated.

If a copy constructor, copy-assignment operator, move constructor, move-


assignment operator, or destructor is explicitly declared, then:

No move constructor is automatically generated.


No move-assignment operator is automatically generated.

7 Note

Additionally, the C++11 standard specifies the following additional rules:

If a copy constructor or destructor is explicitly declared, then automatic


generation of the copy-assignment operator is deprecated.
If a copy-assignment operator or destructor is explicitly declared, then
automatic generation of the copy constructor is deprecated.

In both cases, Visual Studio continues to automatically generate the necessary


functions implicitly, and doesn't emit a warning by default. Since Visual Studio 2022
version 17.7, C5267 can be enabled to emit a warning.

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.

These rules can complicate the implementation of what should be straight-forward,


user-defined types and common C++ idioms—for example, making a user-defined type
noncopyable by declaring the copy constructor and copy-assignment operator privately
and not defining them.

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.

In C++11, the noncopyable idiom can be implemented in a way that is more


straightforward.

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:

Generation of the default constructor is still prevented by declaring the copy


constructor, but you can bring it back by explicitly defaulting it.

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.

You default a special member function by declaring it as in this example:

C++

struct widget
{
widget()=default;

inline widget& operator=(const widget&);


};

inline widget& widget::operator=(const widget&) =default;

Notice that you can default a special member function outside the body of a class as
long as it's inlinable.

Because of the performance benefits of trivial special member functions, we recommend


that you prefer automatically generated special member functions over empty function
bodies when you want the default behavior. You can do this either by explicitly
defaulting the special member function, or by not declaring it (and also not declaring
other special member functions that would prevent it from being automatically
generated.)

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

Deleting of normal member function or nonmember functions prevents problematic


type promotions from causing an unintended function to be called. This works because
deleted functions still participate in overload resolution and provide a better match than
the function that could be called after the types are promoted. The function call resolves
to the more-specific—but deleted—function and causes a compiler error.

C++

// deleted overload prevents call through type promotion of float to double


from succeeding.
void call_with_true_double_only(float) =delete;
void call_with_true_double_only(double param) { return; }

Notice in the preceding sample that calling call_with_true_double_only by using a


float argument would cause a compiler error, but calling call_with_true_double_only

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++

template < typename T >


void call_with_true_double_only(T) =delete; //prevent call through type
promotion of any T to double from succeeding.

void call_with_true_double_only(double param) { return; } // also define for


const double, double&, etc. as needed.
Argument-Dependent Name (Koenig)
Lookup on Functions
Article • 08/03/2021

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++

// Prototype three print functions.


int print( char *s ); // Print a string.
int print( double dvalue ); // Print a double.
int print( double dvalue, int prec ); // Print a double with a
// given precision.

In many applications, a reasonable default can be supplied for prec , eliminating the
need for two functions:

C++

// Prototype two print functions.


int print( char *s ); // Print a string.
int print( double dvalue, int prec=2 ); // Print a double with a
// given precision.

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

// Print a double in specified precision.


// Positive numbers for precision indicate how many digits
// precision after the decimal point to show. Negative
// numbers for precision indicate where to round the number
// to the left of the decimal point.

#include <iostream>
#include <math.h>
using namespace std;

int print( double dvalue, int prec ) {


// Use table-lookup for rounding/truncation.
static const double rgPow10[] = {
10E-7, 10E-6, 10E-5, 10E-4, 10E-3, 10E-2, 10E-1, 10E0,
10E1, 10E2, 10E3, 10E4, 10E5, 10E6
};
const int iPowZero = 6;
// If precision out of range, just print the number.
if( prec >= -6 && prec <= 7 )
// Scale, truncate, then rescale.
dvalue = floor( dvalue / rgPow10[iPowZero - prec] ) *
rgPow10[iPowZero - prec];
cout << dvalue << endl;
return cout.good();
}

To invoke the new print function, use code such as the following:

C++

print( d ); // Precision of 2 supplied by default argument.


print( d, 0 ); // Override default argument to achieve other
// results.

Note these points when using default arguments:

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++

int print( double dvalue = 0.0, int prec );

A default argument cannot be redefined in later declarations even if the


redefinition is identical to the original. Therefore, the following code produces an
error:

C++

// Prototype for print function.


int print( double dvalue, int prec = 2 );

...

// Definition for print function.


int print( double dvalue, int prec = 2 )
{
...
}
The problem with this code is that the function declaration in the definition
redefines the default argument for prec .

Additional default arguments can be added by later declarations.

Default arguments can be provided for pointers to functions. For example:

C++

int (*pShowIntVal)( int i = 0 );


Inline functions (C++)
Article • 01/22/2024

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.

A function defined in the body of a class declaration is implicitly an inline function.

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

optional in the function declarations in the class declaration.

C++

// account.h
class Account
{
public:
Account(double initial_balance)
{
balance = initial_balance;
}

double GetBalance() const;


double Deposit(double amount);
double Withdraw(double amount);

private:
double balance;
};
inline double Account::GetBalance() const
{
return balance;
}

inline double Account::Deposit(double amount)


{
balance += amount;
return balance;
}

inline double Account::Withdraw(double amount)


{
balance -= amount;
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.

inline , __inline , and __forceinline


The inline and __inline specifiers suggest to the compiler that it insert a copy of the
function body into each place the function is called.

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 __inline keyword is equivalent to inline .

Even with __forceinline , the compiler can't inline a function if:

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.

END Microsoft Specific

For more information on using the inline specifier, see:

Inline Class Member Functions


Defining Inline C++ Functions with dllexport and dllimport

When to use inline functions


Inline functions are best used for small functions, such as those that provide access to
data members. Short functions are sensitive to the overhead of function calls. Longer
functions spend proportionately less time in the calling and returning sequence and
benefit less from inlining.

A Point class can be defined as follows:

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

inline unsigned& Point::y()


{
return _y;
}

Assuming coordinate manipulation is a relatively common operation in a client of such a


class, specifying the two accessor functions ( x and y in the preceding example) as
inline typically saves the overhead on:

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

Inline functions vs. macros


A macro has some things in common with an inline function. But there are important
differences. Consider the following example:

C++

#include <iostream>

#define mult1(a, b) a * b
#define mult2(a, b) (a) * (b)
#define mult3(a, b) ((a) * (b))

inline int multiply(int a, int b)


{
return 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

std::cout << mult3(2, 2.2) << std::endl; // no warning


std::cout << multiply(2, 2.2); // Warning C4244 'argument': conversion
from 'double' to 'int', possible loss of data
}

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>

#define sqr(a) ((a) * (a))

int increment(int& number)


{
return number++;
}

inline int square(int a)


{
return a * a;
}

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

In this example, the function increment is called twice as the expression


sqr(increment(c)) expands to ((increment(c)) * (increment(c))) . This caused the

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.

The name of an overloaded operator is operator x, where x is the operator as it appears


in the following table. For example, to overload the addition operator, you define a
function called operator+. Similarly, to overload the addition/assignment operator, +=,
define a function called operator+=.

Redefinable Operators

Operator Name Type

, Comma Binary

! Logical NOT Unary

!= Inequality Binary

% Modulus Binary

%= Modulus assignment Binary

& Bitwise AND Binary

& Address-of Unary

&& Logical AND Binary

&= Bitwise AND assignment Binary


Operator Name Type

() Function call —

() Cast Operator Unary

* Multiplication Binary

* Pointer dereference Unary

*= Multiplication assignment Binary

+ Addition Binary

+ Unary Plus Unary

++ Increment 1 Unary

+= Addition assignment Binary

- Subtraction Binary

- Unary negation Unary

-- Decrement 1 Unary

-= Subtraction assignment Binary

-> Member selection Binary

->* Pointer-to-member selection Binary

/ Division Binary

/= Division assignment Binary

< Less than Binary

<< Left shift Binary

<<= Left shift assignment Binary

<= Less than or equal to Binary

= Assignment Binary

== Equality Binary

> Greater than Binary

>= Greater than or equal to Binary

>> Right shift Binary


Operator Name Type

>>= Right shift assignment Binary

[] Array subscript —

^ Exclusive OR Binary

^= Exclusive OR assignment Binary

| Bitwise inclusive OR Binary

|= Bitwise inclusive OR assignment Binary

|| Logical OR Binary

~ One's complement Unary

delete Delete —

new New —

conversion operators conversion operators Unary

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

Increment and Decrement.

User-Defined Type Conversions

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 convert to string

## 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;
};

// Operator overloaded using a member function


Complex Complex::operator+( Complex &other ) {
return Complex( re + other.re, im + other.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

Overloading Unary Operators

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 define new operators, such as ..

You cannot redefine the meaning of operators when applied to built-in data types.

Overloaded operators must either be a nonstatic class member function or a


global function. A global function that needs access to private or protected class
members must be declared as a friend of that class. A global function must take at
least one argument that is of class or enumerated type or that is a reference to a
class or enumerated type. For example:

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.

Operators obey the precedence, grouping, and number of operands dictated by


their typical use with built-in types. Therefore, there is no way to express the
concept "add 2 and 3 to an object of type Point ," expecting 2 to be added to the x
coordinate and 3 to be added to the y coordinate.
Unary operators declared as member functions take no arguments; if declared as
global functions, they take one argument.

Binary operators declared as member functions take one argument; if declared as


global functions, they take two arguments.

If an operator can be used as either a unary or a binary operator (&, *, +, and -),
you can overload each use separately.

Overloaded operators cannot have default arguments.

All overloaded operators except assignment (operator=) are inherited by derived


classes.

The first argument for member-function overloaded operators is always of the


class type of the object for which the operator is invoked (the class in which the
operator is declared, or a class derived from that class). No conversions are
supplied for the first argument.

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.

Overloadable unary operators


You can overload the following unary operators on user-defined types:

! (logical NOT)

& (address-of)

~ (complement)

* (pointer dereference)

+ (unary plus)

- (unary negation)

++ (prefix increment) or (postfix increment)

-- (prefix decrement) or (postfix decrement)

Conversion operators

Unary operator overload declarations


You can declare overloaded unary operators either as non-static member functions or as
nonmember functions. Overloaded unary member functions take no argument because
they implicitly operate on this . Nonmember functions are declared with one argument.
When both forms are declared, the compiler follows the rules for overload resolution to
determine which function to use, if any.

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:

return-type operator op ();


In this form, return-type is the return type and op is one of the operators listed in the
preceding table.

To declare a unary operator function as a nonmember function, use this declaration


form:

return-type operator op ( class-type );

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:

Preincrement and postincrement

Predecrement and postdecrement

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.

// Declare prefix and postfix decrement operators.


Point& operator--(); // Prefix decrement operator.
Point operator--(int); // Postfix decrement operator.

// Define default constructor.


Point() { _x = _y = 0; }

// Define accessor functions.


int x() { return _x; }
int y() { return _y; }
private:
int _x, _y;
};

// Define prefix increment operator.


Point& Point::operator++()
{
_x++;
_y++;
return *this;
}

// Define postfix increment operator.


Point Point::operator++(int)
{
Point temp = *this;
++*this;
return temp;
}

// Define prefix decrement operator.


Point& Point::operator--()
{
_x--;
_y--;
return *this;
}

// Define postfix decrement operator.


Point Point::operator--(int)
{
Point temp = *this;
--*this;
return temp;
}

int main()
{
}

The same operators can be defined in file scope (globally) using the following function
prototypes:

C++

friend Point& operator++( Point& ); // Prefix increment


friend Point operator++( Point&, int ); // Postfix increment
friend Point& operator--( Point& ); // Prefix decrement
friend Point operator--( Point&, int ); // Postfix decrement
The argument of type int that denotes the postfix form of the increment or decrement
operator isn't commonly used to pass arguments. It usually contains the value 0.
However, it can be used as follows:

C++

// increment_and_decrement2.cpp
class Int
{
public:
Int operator++( int n ); // Postfix increment operator
private:
int _i;
};

Int Int::operator++( int n )


{
Int result = *this;
if( n != 0 ) // Handle case where an argument is passed.
_i += n;
else
_i++; // Handle case where no argument is passed.
return result;
}

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

The following table shows a list of operators that can be overloaded.

Redefinable Binary Operators


Operator Name

, Comma

!= Inequality

% Modulus

%= Modulus/assignment

& Bitwise AND

&& Logical AND

&= Bitwise AND/assignment

* Multiplication

*= Multiplication/assignment

+ Addition

+= Addition/assignment

- Subtraction

-= Subtraction/assignment

-> Member selection

->* Pointer-to-member selection

/ Division

/= Division/assignment

< Less than

<< Left shift

<<= Left shift/assignment


Operator Name

<= Less than or equal to

= Assignment

== Equality

> Greater than

>= Greater than or equal to

>> Right shift

>>= Right shift/assignment

^ Exclusive OR

^= Exclusive OR/assignment

| Bitwise inclusive OR

|= Bitwise inclusive OR/assignment

|| Logical OR

To declare a binary operator function as a nonstatic member, you must declare it in the
form:

ret-type operator op ( arg )

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:

ret-type operator op ( arg1, arg2 )

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:

It must be a nonstatic member function. No operator= can be declared as a


nonmember function.
It is not inherited by derived classes.
A default operator= function can be generated by the compiler for class types, if
none exists.

The following example illustrates how to declare an assignment operator:

C++

class Point
{
public:
int _x, _y;

// Right side of copy assignment is the argument.


Point& operator=(const Point&);
};

// Define copy assignment operator.


Point& Point::operator=(const Point& otherPoint)
{
_x = otherPoint._x;
_y = otherPoint._y;

// Assignment operator returns left side of assignment.


return *this;
}

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++

// Copy constructor is called--not overloaded copy assignment operator!


Point pt3 = pt1;

// The previous initialization is similar to the following:


Point pt4(pt1); // Copy constructor call.

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

The function-call operator, invoked using parentheses, is a binary operator.

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

Given an appropriate overloaded function-call operator, however, this syntax can be


used to offset the x coordinate 3 units and the y coordinate 2 units. The following code
shows such a definition:

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>

using namespace std;


class IntVector {
public:
IntVector( int cElements );
~IntVector() { delete [] _iElements; }
int& operator[](int nSubscript);
private:
int *_iElements;
int _iUpperBound;
};

// Construct an IntVector.
IntVector::IntVector( int cElements ) {
_iElements = new int[cElements];
_iUpperBound = cElements;
}

// Subscript operator for IntVector.


int& IntVector::operator[](int nSubscript) {
static int iErr = -1;

if( nSubscript >= 0 && nSubscript < _iUpperBound )


return _iElements[nSubscript];
else {
clog << "Array bounds violation." << endl;
return iErr;
}
}
// Test the IntVector class.
int main() {
IntVector v( 10 );
int i;

for( i = 0; i <= 10; ++i )


v[i] = i;

v[3] = v[9];

for ( i = 0; i <= 10; ++i )


cout << "Element: [" << i << "] = " << v[i] << endl;
}

Output

Array bounds violation.


Element: [0] = 0
Element: [1] = 1
Element: [2] = 2
Element: [3] = 9
Element: [4] = 4
Element: [5] = 5
Element: [6] = 6
Element: [7] = 7
Element: [8] = 8
Element: [9] = 9
Array bounds violation.
Element: [10] = 10

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.

This operator is used (often in conjunction with the pointer-dereference operator) to


implement "smart pointers" that validate pointers prior to dereference or count usage.

The . member access operator cannot be overloaded.

See also
Operator Overloading

You might also like