Enum
enum name { enumerator = constexpr , enumerator = constexpr , … } |
enum name : type { enumerator = constexpr , enumerator = constexpr , … } |
enum name : type ; |
enum A{
One=0,
Two=1,
Three=3
};
class AA{
public:
enum A{
One=0,
Two=1,
Three=3
};
};
int main(int _argc,char* _argv[]){
int l=::A::One;
l=::One;
::A a=(::A)1;
AA::A aa=AA::One;
aa=AA::A::One;
}
enum num : char { one = '0' };
std::cout << num::one; // '0', not 48
enum Foo { a, b, c = 10, d, e = 1, f, g = f + c };
//a = 0, b = 1, c = 10, d = 11, e = 1, f = 2, g = 12
Array
int a[2]; // array of 2 int
int* p1 = a; // a decays to a pointer to the first element of a
int b[2][3]; // array of 2 arrays of 3 int
// int** p2 = b; // error: b does not decay to int**
int (*p2)[3] = b; // b decays to a pointer to the first 3-element row of b
int c[2][3][4]; // array of 2 arrays of 3 arrays of 4 int
// int*** p3 = c; // error: c does not decay to int***
int (*p3)[3][4] = c; // c decays to a pointer to the first 3 × 4-element plane of c
int l[]{1,2,3,4,5,6,7};//or = {1,2,3,4,5,6,7};
int m=*(l+4);
//l++ ++l errors
int* k=new int[5]{1,2,3,4,5};
void g(int (&a)[3])
{
std::cout << a[0] << '\n';
}
void f(int* p)
{
std::cout << *p << '\n';
}
int main()
{
int a[3] = {1, 2, 3};
int* p = a;
std::cout << sizeof a << '\n' // prints size of array
<< sizeof p << '\n'; // prints size of a pointer
// where arrays are acceptable, but pointers aren't, only arrays may be used
g(a); // okay: function takes an array by reference
// g(p); // error
for(int n: a) // okay: arrays can be used in range-for loops
std::cout << n << ' '; // prints elements of the array
// for(int n: p) // error
// std::cout << n << ' ';
// where pointers are acceptable, but arrays aren't, both may be used:
f(a); // okay: function takes a pointer
f(p); // okay: function takes a pointer
std::cout << *a << '\n' // prints the first element
<< *p << '\n' // same
<< *(a + 1) << ' ' << a[1] << '\n' // prints the second element
<< *(p + 1) << ' ' << p[1] << '\n'; // same
}
Namespace
namespace Q {
namespace V { // original-namespace-definition for V
void f(); // declaration of Q::V::f
}
void V::f() {} // OK
void V::g() {} // Error: g() is not yet a member of V
namespace V { // extension-namespace-definition for V
void g(); // declaration of Q::V::g
}
}
namespace R { // not a enclosing namespace for Q
void Q::V::g() {} // Error: cannot define Q::V::g inside R
}
void Q::V::g() {} // OK: global namespace encloses Q
namespace D {
int d1;
void f(char);
}
using namespace D; // introduces D::d1, D::f, D::d2, D::f,
// E::e, and E::f into global namespace!
int d1; // OK: no conflict with D::d1 when declaring
namespace E {
int e;
void f(int);
}
namespace D { // namespace extension
int d2;
using namespace E; // transitive using-directive
void f(int);
}
void f() {
d1++; // error: ambiguous ::d1 or D::d1?
::d1++; // OK
D::d1++; // OK
d2++; // OK, d2 is D::d2
e++; // OK: e is E::e due to transitive using
f(1); // error: ambiguous: D::f(int) or E::f(int)?
f('a'); // OK: the only f(char) is D::f(char)
}
namespace vec {
template< typename T >
class vector {
// ...
};
} // of vec
int main()
{
std::vector<int> v1; // Standard vector.
vec::vector<int> v2; // User defined vector.
v1 = v2; // Error: v1 and v2 are different object's type.
{
using namespace std;
vector<int> v3; // Same as std::vector
v1 = v3; // OK
}
{
using vec::vector;
vector<int> v4; // Same as vec::vector
v2 = v4; // OK
}
return 0;
}
namespace foo {
namespace bar {
namespace baz {
int qux = 42;
}
}
}
namespace fbz = foo::bar::baz;
int main()
{
std::cout << fbz::qux << '\n';
}
Reference declaration
int& a[3]; // error
int&* p; // error
int& &r; // error
typedef int& lref;
typedef int&& rref;
int n;
lref& r1 = n; // type of r1 is int&
lref&& r2 = n; // type of r2 is int&
rref& r3 = n; // type of r3 is int&
rref&& r4 = 1; // type of r4 is int&&
void f(int& x) {
std::cout << "lvalue reference overload f(" << x << ")\n";
}
void f(const int& x) {
std::cout << "lvalue reference to const overload f(" << x << ")\n";
}
void f(int&& x) {
std::cout << "rvalue reference overload f(" << x << ")\n";
}
int main() {
int i = 1;
const int ci = 2;
f(i); // calls f(int&)
f(ci); // calls f(const int&)
f(3); // calls f(int&&)
// would call f(const int&) if f(int&&) overload wasn't provided
f(std::move(i)); // calls f(int&&)
// rvalue reference variables are lvalues when used in expressions
int&& x = 1;
f(x); // calls f(int& x)
f(std::move(x)); // calls f(int&& x)
}
forwarding
template<class T>
int f(T&& x) { // x is a forwarding reference
return g(std::forward<T>(x)); // and so can be forwarded
}
int main() {
int i;
f(i); // argument is lvalue, calls f<int&>(int&), std::forward<int&>(x) is lvalue
f(0); // argument is rvalue, calls f<int>(int&&), std::forward<int>(x) is rvalue
}
template<class T>
int g(const T&& x); // x is not a forwarding reference: const T is not cv-unqualified
template<class T> struct A {
template<class U>
A(T&& x, U&& y, int* p); // x is not a forwarding reference: T is not a
// type template parameter of the constructor,
// but y is a forwarding reference
};
Pointer
pointer to object
struct C {
int x, y;
} c;
int* px = &c.x; // value of px is "pointer to c.x"
int* pxe= px + 1; // value of pxe is "pointer past the end of c.x"
int* py = &c.y; // value of py is "pointer to c.y"
assert(pxe == py); // == tests if two pointers represent the same address
// may or may not fire
*pxe = 1; // undefined behavior even if the assertion does not fire
int n;
int* np = &n; // pointer to int
int* const* npp = &np; // non-const pointer to const pointer to non-const int
int a[2];
int (*ap)[2] = &a; // pointer to array of int
struct S { int n; };
S s = {1};
int* sp = &s.n; // pointer to the int that is a member of s
int n;
int* p = &n; // pointer to n
int& r = *p; // reference is bound to the lvalue expression that identifies n
r = 7; // stores the int 7 in n
std::cout << *p; // lvalue-to-rvalue implicit conversion reads the value from n
int a[2];
int* p1 = a; // pointer to the first element a[0] (an int) of the array a
int b[6][3][8];
int (*p2)[3][8] = b; // pointer to the first element b[0] of the array b,
// which is an array of 3 arrays of 8 ints
struct Base {};
struct Derived : Base {};
Derived d;
Base* p = &d;
const pointer
Syntax | meaning |
---|---|
const T* | pointer to constant object |
T const* | pointer to constant object |
T* const | constant pointer to object |
const T* const | constant pointer to constant object |
T const* const | constant pointer to constant object |
const int* const i=new int ;
const int* const* ii=&i;//non-const pointer to const pointer to const object
const int n1=12;
const int* n2=&n1;//int* is error
pointer to void
int n = 1;
int* p1 = &n;
void* pv = p1;
int* p2 = static_cast<int*>(pv);
std::cout << *p2 << '\n'; // prints 1
pointer to functions
int (* f)() | f is a pointer to a function with prototype int func(). |
int (* f())() | f is a function taking no inputs and returning a pointer to a function with prototype int func(). |
int * f() | f is a function returning a pointer-to-int. |
int (* a[])() | a is an array of pointers to functions each with prototype int func(). |
int (* f())[] | f is a function returning a pointer to an array. |
int (f[])() | Not allowed. Can’t have an array of functions. |
int * const *(*g)(float) | g is pointer to a function with prototype int * const * func(float) where its return value int * const * is a pointer to a read-only pointer-to-int. |
int f();
int (*p)() = f; // pointer p is pointing to f
int (&r)() = *p; // the lvalue that identifies f is bound to a reference
r(); // function f invoked through lvalue reference
(*p)(); // function f invoked through the function lvalue
p();
Pointers to data members
struct C { int m; };
int main()
{
int C::* p = &C::m; // pointer to data member m of class C
C c = {7};
std::cout << c.*p << '\n'; // prints 7
C* cp = &c;
cp->m = 10;
std::cout << cp->*p << '\n'; // prints 10
}
struct Base { int m; };
struct Derived : Base {};
int main()
{
int Base::* bp = &Base::m;
int Derived::* dp = bp;
Derived d;
d.m = 1;
std::cout << d.*dp << ' ' << d.*bp << '\n'; // prints 1 1
}
struct A
{
int m;
// const pointer to non-const member
int A::* const p;
};
int main()
{
// non-const pointer to data member which is a const pointer to non-const member
int A::* const A::* p1 = &A::p;
const A a = {1, &A::m};
std::cout << a.*(a.*p1) << '\n'; // prints 1
// regular non-const pointer to a const pointer-to-member
int A::* const* p2 = &a.p;
std::cout << a.**p2 << '\n'; // prints 1
}
Pointers to function members
struct Base
{
void f(int n) { std::cout << n << '\n'; }
};
struct Derived : Base {};
int main()
{
void (Base::* bp)(int) = &Base::f;
void (Derived::* dp)(int) = bp;
Derived d;
(d.*dp)(1);
(d.*bp)(2);
}
struct Base {};
struct Derived : Base
{
void f(int n) { std::cout << n << '\n'; }
};
int main()
{
void (Derived::* dp)(int) = &Derived::f;
void (Base::* bp)(int) = static_cast<void (Base::*)(int)>(dp);
Derived d;
(d.*bp)(1); // okay: prints 1
Base b;
(b.*bp)(2); // undefined behavior
}
null pointer
To initialize a pointer to null or to assign the null value to an existing pointer, the null pointer literal nullptr, the null pointer constant NULL, or the implicit conversion from the integer value 0 may be used.
const and volatile and Mutable
int main()
{
int n1 = 0; // non-const object
const int n2 = 0; // const object
int const n3 = 0; // const object (same as n2)
volatile int n4 = 0; // volatile object
const struct
{
int n1;
mutable int n2;
} x = {0, 0}; // const object with mutable member
n1 = 1; // ok, modifiable object
// n2 = 2; // error: non-modifiable object
n4 = 3; // ok, treated as a side-effect
// x.n1 = 4; // error: member of a const object is const
x.n2 = 4; // ok, mutable member of a const object isn't const
const int& r1 = n1; // reference to const bound to non-const object
// r1 = 2; // error: attempt to modify through reference to const
const_cast<int&>(r1) = 2; // ok, modifies non-const object n1
const int& r2 = n2; // reference to const bound to const object
// r2 = 2; // error: attempt to modify through reference to const
// const_cast<int&>(r2) = 2; // undefined behavior: attempt to modify const object n2
}
class Test {
public:
int x;
mutable int y;
Test() { x = 4; y = 10; }
};
int main()
{
const Test t1;
t1.y = 20;
cout << t1.y;
return 0;
}
class Customer
{
char name[25];
mutable char placedorder[50];
int tableno;
mutable int bill;
public:
Customer(char* s, char* m, int a, int p)
{
strcpy(name, s);
strcpy(placedorder, m);
tableno = a;
bill = p;
}
void changePlacedOrder(char* p) const
{
strcpy(placedorder, p);
}
void changeBill(int s) const
{
bill = s;
}
void display() const
{
cout << "Customer name is: " << name << endl;
cout << "Food ordered by customer is: " << placedorder << endl;
cout << "table no is: " << tableno << endl;
cout << "Total payable amount: " << bill << endl;
}
};
constexpr specifier
The constexpr
specifier declares that it is possible to evaluate the value of the function or variable at compile time.
#include <iostream>
#include <stdexcept>
// C++11 constexpr functions use recursion rather than iteration
// (C++14 constexpr functions may use local variables and loops)
constexpr int factorial(int n)
{
return n <= 1 ? 1 : (n * factorial(n - 1));
}
// literal class
class conststr {
const char* p;
std::size_t sz;
public:
template<std::size_t N>
constexpr conststr(const char(&a)[N]): p(a), sz(N - 1) {}
// constexpr functions signal errors by throwing exceptions
// in C++11, they must do so from the conditional operator ?:
constexpr char operator[](std::size_t n) const
{
return n < sz ? p[n] : throw std::out_of_range("");
}
constexpr std::size_t size() const { return sz; }
};
// C++11 constexpr functions had to put everything in a single return statement
// (C++14 doesn't have that requirement)
constexpr std::size_t countlower(conststr s, std::size_t n = 0,
std::size_t c = 0)
{
return n == s.size() ? c :
'a' <= s[n] && s[n] <= 'z' ? countlower(s, n + 1, c + 1) :
countlower(s, n + 1, c);
}
// output function that requires a compile-time constant, for testing
template<int n>
struct constN
{
constN() { std::cout << n << '\n'; }
};
int main()
{
std::cout << "4! = " ;
constN<factorial(4)> out1; // computed at compile time
volatile int k = 8; // disallow optimization using volatile
std::cout << k << "! = " << factorial(k) << '\n'; // computed at run time
std::cout << "the number of lowercase letters in \"Hello, world!\" is ";
constN<countlower("Hello, world!")> out2; // implicitly converted to conststr
}
decltype
struct A { double x; };
const A* a;
decltype(a->x) y; // type of y is double (declared type)
decltype((a->x)) z = y; // type of z is const double& (lvalue expression)
template<typename T, typename U>
auto add(T t, U u) -> decltype(t + u) // return type depends on template parameters
// return type can be deduced since C++14
{
return t+u;
}
int main()
{
int i = 33;
decltype(i) j = i * 2;
std::cout << "i = " << i << ", "
<< "j = " << j << '\n';
auto f = [](int a, int b) -> int
{
return a * b;
};
decltype(f) g = f; // the type of a lambda function is unique and unnamed
i = f(2, 2);
j = g(3, 3);
std::cout << "i = " << i << ", "
<< "j = " << j << '\n';
}
auto
#include <iostream>
#include <utility>
template<class T, class U>
auto add(T t, U u) { return t + u; } // the return type is the type of operator+(T, U)
// perfect forwarding of a function call must use decltype(auto)
// in case the function it calls returns by reference
template<class F, class... Args>
decltype(auto) PerfectForward(F fun, Args&&... args)
{
return fun(std::forward<Args>(args)...);
}
template<auto n> // C++17 auto parameter declaration
auto f() -> std::pair<decltype(n), decltype(n)> // auto can't deduce from brace-init-list
{
return {n, n};
}
int main()
{
auto a = 1 + 2; // type of a is int
auto b = add(1, 1.2); // type of b is double
static_assert(std::is_same_v<decltype(a), int>);
static_assert(std::is_same_v<decltype(b), double>);
auto c0 = a; // type of c0 is int, holding a copy of a
decltype(auto) c1 = a; // type of c1 is int, holding a copy of a
decltype(auto) c2 = (a); // type of c2 is int&, an alias of a
std::cout << "a, before modification through c2 = " << a << '\n';
++c2;
std::cout << "a, after modification through c2 = " << a << '\n';
auto [v, w] = f<0>(); //structured binding declaration
std::cout<<v<<w;
auto d = {1, 2}; // OK: type of d is std::initializer_list<int>
auto n = {5}; // OK: type of n is std::initializer_list<int>
// auto e{1, 2}; // Error as of C++17, std::initializer_list<int> before
auto m{5}; // OK: type of m is int as of C++17, initializer_list<int> before
// decltype(auto) z = { 1, 2 } // Error: {1, 2} is not an expression
// auto is commonly used for unnamed types such as the types of lambda expressions
auto lambda = [](int x) { return x + 3; };
// auto int x; // valid C++98, error as of C++11
// auto x; // valid C, error in C++
}
decltype(auto)
in generic code you want to be able to perfectly forward a return type without knowing whether you are dealing with a reference or a value. decltype(auto)
gives you that ability:
int i;
int&& f();
auto x3a = i; // decltype(x3a) is int
decltype(auto) x3d = i; // decltype(x3d) is int
auto x4a = (i); // decltype(x4a) is int
decltype(auto) x4d = (i); // decltype(x4d) is int&
auto x5a = f(); // decltype(x5a) is int
decltype(auto) x5d = f(); // decltype(x5d) is int&&
auto x6a = { 1, 2 }; // decltype(x6a) is std::initializer_list<int>
decltype(auto) x6d = { 1, 2 }; // error, { 1, 2 } is not an expression
auto *x7a = &i; // decltype(x7a) is int*
decltype(auto)*x7d = &i; // error, declared type is not plain decltype(auto)
template<class Fun, class... Args>
decltype(auto) Example(Fun fun, Args&&... args)
{
return fun(std::forward<Args>(args)...);
}
Delaying return type deduction in recursive templates
template<int i>
struct Int {};
constexpr auto iter(Int<0>) -> Int<0>;
template<int i>
constexpr auto iter(Int<i>) -> decltype(auto)
{ return iter(Int<i-1>{}); }
int main() { decltype(iter(Int<10>{})) a; }
Elaborated type
enum A { A_START = 0 };
void A(enum A a) {}
int main() {
enum A a;
A( a );
}
template <typename T>
struct Node {
struct Node* Next; // OK: lookup of Node finds the injected-class-name
struct Data* Data; // OK: declares type Data at global scope
// and also declares the data member Data
friend class ::List; // error: cannot introduce a qualified name
enum Kind* kind; // error: cannot introduce an enum
};
Data* p; // OK: struct Data has been declared