if statement
Conditionally executes another statement.
Used where code needs to be executed based on a run-time or compile-time condition.
Syntax
attr(optional) if ( condition ) statement-true | (until C++17) | |
attr(optional) if ( condition ) statement-true else statement-false | (until C++17) | |
attr(optional) if constexpr(optional) ( init-statement(optional) condition ) statement-true | (since C++17) | |
attr(optional) if constexpr(optional) ( init-statement(optional) condition ) statement-true else statement-false | (since C++17) |
| attr(C++11) | - | any number of attributes |
| condition | - | one of
|
| init-statement(C++17) | - | either
;, which is why it is often described informally as an expression or a declaration followed by a semicolon. |
| statement-true | - | any statement (often a compound statement), which is executed if condition evaluates to true |
| statement-false | - | any statement (often a compound statement), which is executed if condition evaluates to false |
Explanation
If the condition yields true after conversion to bool, statement-true is executed.
If the else part of the if statement is present and condition yields false after conversion to bool, statement-false is executed.
In the second form of if statement (the one including else), if statement-true is also an if statement then that inner if statement must contain an else part as well (in other words, in nested if-statements, the else is associated with the closest if that doesn't have an else).
#include <iostream>
int main() {
// simple if-statement with an else clause
int i = 2;
if (i > 2) {
std::cout << i << " is greater than 2\n";
} else {
std::cout << i << " is not greater than 2\n";
}
// nested if-statement
int j = 1;
if (i > 1)
if (j > 2)
std::cout << i << " > 1 and " << j << " > 2\n";
else // this else is part of if (j > 2), not of if (i > 1)
std::cout << i << " > 1 and " << j << " <= 2\n";
// declarations can be used as conditions with dynamic_cast
struct Base {
virtual ~Base() {}
};
struct Derived : Base {
void df() { std::cout << "df()\n"; }
};
Base* bp1 = new Base;
Base* bp2 = new Derived;
if (Derived* p = dynamic_cast<Derived*>(bp1)) // cast fails, returns nullptr
p->df(); // not executed
if (auto p = dynamic_cast<Derived*>(bp2)) // cast succeeds
p->df(); // executed
}Output:
2 is not greater than 2 2 > 1 and 1 <= 2 df()
If Statements with InitializerIf init-statement is used, the if statement is equivalent to.
or.
Except that names declared by the init-statement (if init-statement is a declaration) and names declared by condition (if condition is a declaration) are in the same scope, which is also the scope of both statements. std::map<int, std::string> m;
std::mutex mx;
extern bool shared_flag; // guarded by mx
int demo() {
if (auto it = m.find(10); it != m.end()) { return it->second.size(); }
if (char buf[10]; std::fgets(buf, 10, stdin)) { m[0] += buf; }
if (std::lock_guard lock(mx); shared_flag) { unsafe_ping(); shared_flag = false; }
if (int s; int count = ReadBytesWithSignal(&s)) { publish(count); raise(s); }
if (auto keywords = {"if", "for", "while"};
std::any_of(keywords.begin(), keywords.end(),
[&s](const char* kw) { return s == kw; })) {
std::cerr << "Token must not be a keyword\n";
}
} | (since C++17) |
Constexpr IfThe statement that begins with In a constexpr if statement, the value of condition must be a contextually converted constant expression of type The return statements in a discarded statement do not participate in function return type deduction: template <typename T>
auto get_value(T t) {
if constexpr (std::is_pointer_v<T>)
return *t; // deduces return type to int for T = int*
else
return t; // deduces return type to int for T = int
}The discarded statement can odr-use a variable that is not defined. extern int x; // no definition of x required
int f() {
if constexpr (true)
return 0;
else if (x)
return x;
else
return -x;
}If a constexpr if statement appears inside a templated entity, and if condition is not value-dependent after instantiation, the discarded statement is not instantiated when the enclosing template is instantiated . template<typename T, typename ... Rest>
void g(T&& p, Rest&& ...rs) {
// ... handle p
if constexpr (sizeof...(rs) > 0)
g(rs...); // never instantiated with an empty argument list.
}Outside a template, a discarded statement is fully checked. void f() {
if constexpr(false) {
int i = 0;
int *p = i; // Error even though in discarded statement
}
}
template<class T> void g() {
auto lm = [](auto p) {
if constexpr (sizeof(T) == 1 && sizeof p == 1) {
// this condition remains value-dependent after instantiation of g<T>
}
};
}Note: the discarded statement can't be ill-formed for every possible specialization: template <typename T>
void f() {
if constexpr (std::is_arithmetic_v<T>)
// ...
else
static_assert(false, "Must be arithmetic"); // ill-formed: invalid for every T
}The common workaround for such a catch-all statement is a type-dependent expression that is always false: template<class T> struct dependent_false : std::false_type {};
template <typename T>
void f() {
if constexpr (std::is_arithmetic_v<T>)
// ...
else
static_assert(dependent_false<T>::value, "Must be arithmetic"); // ok
}Labels (goto targets, | (since C++17) |
Notes
If statement_true or statement_false is not a compound statement, it is treated as if it were:
if (x)
int i;
// i is no longer in scopeis the same as.
if (x) {
int i;
} // i is no longer in scopeThe scope of the name introduced by condition, if it is a declaration, is the combined scope of both statements' bodies:
if (int x = f()) {
int x; // error: redeclaration of x
} else {
int x; // error: redeclaration of x
}| If statement-true is entered by goto or | (since C++14) |
| Switch and goto are not allowed to jump into a branch of constexpr if statement. | (since C++17) |
Keywords
See Also
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
http://en.cppreference.com/w/cpp/language/if