init-declarator-list: init-declarator init-declarator-list , init-declarator
init-declarator: declarator initializer
T D1, D2, ... Dn;is usually equivalent to
T D1; T D2; ... T Dn;where T is a decl-specifier-seq and each Di is an init-declarator.
struct S { ... };
S S, T;                 // declare two instances of struct S
which is not equivalent to
struct S { ... };
S S;
S T;                    // error
Another exception is when T is auto ([dcl.spec.auto]),
for example:
auto i = 1, j = 2.0;    // error: deduced types for i and j do not match
as opposed to
auto i = 1; // OK: i deduced to have type int auto j = 2.0; // OK: j deduced to have type double
declarator: ptr-declarator noptr-declarator parameters-and-qualifiers trailing-return-type
ptr-declarator: noptr-declarator ptr-operator ptr-declarator
noptr-declarator: declarator-id attribute-specifier-seq noptr-declarator parameters-and-qualifiers noptr-declarator [ constant-expression ] attribute-specifier-seq ( ptr-declarator )
parameters-and-qualifiers: ( parameter-declaration-clause ) cv-qualifier-seq ref-qualifier noexcept-specifier attribute-specifier-seq
trailing-return-type: -> type-id
ptr-operator: * attribute-specifier-seq cv-qualifier-seq & attribute-specifier-seq && attribute-specifier-seq nested-name-specifier * attribute-specifier-seq cv-qualifier-seq
cv-qualifier-seq: cv-qualifier cv-qualifier-seq
cv-qualifier: const volatile
ref-qualifier: & &&
declarator-id: ... id-expression
type-id: type-specifier-seq abstract-declarator
defining-type-id: defining-type-specifier-seq abstract-declarator
abstract-declarator: ptr-abstract-declarator noptr-abstract-declarator parameters-and-qualifiers trailing-return-type abstract-pack-declarator
ptr-abstract-declarator: noptr-abstract-declarator ptr-operator ptr-abstract-declarator
noptr-abstract-declarator: noptr-abstract-declarator parameters-and-qualifiers noptr-abstract-declarator [ constant-expression ] attribute-specifier-seq ( ptr-abstract-declarator )
abstract-pack-declarator: noptr-abstract-pack-declarator ptr-operator abstract-pack-declarator
noptr-abstract-pack-declarator: noptr-abstract-pack-declarator parameters-and-qualifiers noptr-abstract-pack-declarator [ constant-expression ] attribute-specifier-seq ...
int // int i int * // int *pi int *[3] // int *p[3] int (*)[3] // int (*p3i)[3] int *() // int *f() int (*)(double) // int (*pf)(double)name respectively the types “int”, “pointer to int”, “array of 3 pointers to int”, “pointer to array of 3 int”, “function of (no parameters) returning pointer to int”, and “pointer to a function of (double) returning int”.
struct S {
  S(int);
};
void foo(double a) {
  S w(int(a));                  // function declaration
  S x(int());                   // function declaration
  S y((int(a)));                // object declaration
  S y((int)a);                  // object declaration
  S z = int(a);                 // object declaration
} — end example
template <class T> struct X {};
template <int N> struct Y {};
X<int()> a;                     // type-id
X<int(1)> b;                    // expression (ill-formed)
Y<int()> c;                     // type-id (ill-formed)
Y<int(1)> d;                    // expression
void foo(signed char a) {
  sizeof(int());                // type-id (ill-formed)
  sizeof(int(a));               // expression
  sizeof(int(unsigned(a)));     // type-id (ill-formed)
  (int())+1;                    // type-id (ill-formed)
  (int(a))+1;                   // expression
  (int(unsigned(a)))+1;         // type-id (ill-formed)
} — end example
class C { };
void f(int(C)) { }              // void f(int(*fp)(C c)) { }
                                // not: void f(int C) { }
int g(C);
void foo() {
  f(1);                         // error: cannot convert 1 to function pointer
  f(g);                         // OK
}
class C { };
void h(int *(C[10]));           // void h(int *(*_fp)(C _parm[10]));
                                // not: void h(int *C[10]);
 — end exampleT D
T D
( D1 )
T D1Parentheses do not alter the type of the embedded declarator-id, but they can alter the binding of complex declarators.
* attribute-specifier-seq cv-qualifier-seq D1
i = ci; *cp = ci; pc++; pc = cpc; pc = p; ppc = &pc;
ci = 1; // error ci++; // error *pc = 2; // error cp = &ci; // error cpc++; // error p = pc; // error ppc = &p; // error
*ppc = &ci; // OK, but would make p point to ci because of previous error *p = 5; // clobber ci— end example
& attribute-specifier-seq D1 && attribute-specifier-seq D1
typedef int& A;
const A aref = 3;   // ill-formed; lvalue reference to non-const initialized with rvalue
 — end example
void f(double& a) { a += 3.14; }
// ...
double d = 0;
f(d);int v[20]; // ... int& g(int i) { return v[i]; } // ... g(3) = 7;
struct link {
  link* next;
};
link* first;
void h(link*& p) {  // p is a reference to pointer
  p->next = first;
  first = p;
  p = 0;
}
void k() {
   link* q = new link;
   h(q);
} int i; typedef int& LRI; typedef int&& RRI; LRI& r1 = i; // r1 has the type int& const LRI& r2 = i; // r2 has the type int& const LRI&& r3 = i; // r3 has the type int& RRI& r4 = i; // r4 has the type int& RRI&& r5 = 5; // r5 has the type int&& decltype(r2)& r6 = i; // r6 has the type int& decltype(r2)&& r7 = i; // r7 has the type int&— end example
nested-name-specifier * attribute-specifier-seq cv-qualifier-seq D1
struct X {
  void f(int);
  int a;
};
struct Y;
int X::* pmi = &X::a;
void (X::* pmf)(int) = &X::f;
double X::* pmd;
char Y::* pmc;X obj; // ... obj.*pmi = 7; // assign 7 to an integer member of obj (obj.*pmf)(7); // call a function member of obj with the argument 7
D1 [ constant-expression ] attribute-specifier-seq
typedef int A[5], AA[2][3]; typedef const A CA; // type is “array of 5 const int” typedef const AA CAA; // type is “array of 2 array of 3 const int”— end example
float fa[17], *afp[17];
static int x3d[3][5][7];
extern int x[10];
struct S {
  static int y[10];
};
int x[];                // OK: bound is 10
int S::y[];             // OK: bound is 10
void f() {
  extern int x[];
  int i = sizeof(x);    // error: incomplete object type
}int x[3][5];
D1 ( parameter-declaration-clause ) cv-qualifier-seq ref-qualifier noexcept-specifier attribute-specifier-seqand the type of the contained declarator-id in the declaration T D1 is “derived-declarator-type-list T”, the type of the declarator-id in D is “derived-declarator-type-list noexcept function of (parameter-declaration-clause) cv-qualifier-seq ref-qualifier returning T”, where the optional noexcept is present if and only if the exception specification ([except.spec]) is non-throwing.
D1 ( parameter-declaration-clause ) cv-qualifier-seq ref-qualifier noexcept-specifier attribute-specifier-seq trailing-return-type
parameter-declaration-clause: parameter-declaration-list ... parameter-declaration-list , ...
parameter-declaration-list: parameter-declaration parameter-declaration-list , parameter-declaration
parameter-declaration: attribute-specifier-seq decl-specifier-seq declarator attribute-specifier-seq decl-specifier-seq declarator = initializer-clause attribute-specifier-seq decl-specifier-seq abstract-declarator attribute-specifier-seq decl-specifier-seq abstract-declarator = initializer-clause
int printf(const char*, ...);
printf("hello world");
printf("a=%d b=%d", a, b);typedef int FIC(int) const; FIC f; // ill-formed: does not declare a member function struct S { FIC f; // OK }; FIC S::*pm = &S::f; // OK— end example
typedef void F();
struct S {
  const F f;        // OK: equivalent to: void f();
}; — end exampletypedef void F(); F fv; // OK: equivalent to void fv(); F fv { } // ill-formed void fv() { } // OK: definition of fv— end example
int i,
    *pi,
    f(),
    *fpi(int),
    (*pif)(const char*, const char*),
    (*fpif(int))(int);typedef int IFUNC(int); IFUNC* fpif(int);
auto fpif(int)->int(*)(int);
template <class T, class U> auto add(T t, U u) -> decltype(t + u);
template <class T, class U> decltype((*(T*)0) + (*(U*)0)) add(T t, U u);— end note
template<typename... T> void f(T (* ...t)(int, int));
int add(int, int);
float subtract(int, int);
void g() {
  f(add, subtract);
} — end examplevoid g(int = 0, ...); // OK, ellipsis is not a parameter so it can follow // a parameter with a default argument void f(int, int); void f(int, int = 7); void h() { f(3); // OK, calls f(3, 7) void f(int = 1, int); // error: does not use default from surrounding scope } void m() { void f(int, int); // has no defaults f(4); // error: wrong number of arguments void f(int, int = 5); // OK f(4); // OK, calls f(4, 5); void f(int, int = 5); // error: cannot redefine, even to same value } void n() { f(6); // OK, calls f(6, 7) }— end example
int a = 1; int f(int); int g(int x = f(a)); // default argument: f(::a) void h() { a = 2; { int a = 3; g(); // g(f(::a)) } }— end example
class C {
  void f(int i = 3);
  void g(int i, int j = 99);
};
void C::f(int i = 3) {}         // error: default argument already specified in class scope
void C::g(int i = 88, int j) {} // in this translation unit, C::g can be called with no argument
 — end example
void f() {
  int i;
  extern void g(int x = i);         // error
  extern void h(int x = sizeof(i)); // OK
  // ...
} — end exampleint a; int f(int a, int b = a); // error: parameter a used as default argument typedef int I; int g(float I, int b = I(2)); // error: parameter I found int h(int a, int b = sizeof(a)); // OK, unevaluated operand— end example
int b;
class X {
  int a;
  int mem1(int i = a);              // error: non-static member a used as default argument
  int mem2(int i = b);              // OK;  use X::b
  static int b;
}; 
int f(int = 0);
void h() {
  int j = f(1);
  int k = f();                      // OK, means f(0)
}
int (*p1)(int) = &f;
int (*p2)() = &f;                   // error: type mismatch
 — end example
struct A {
  virtual void f(int a = 7);
};
struct B : public A {
  void f(int a);
};
void m() {
  B* pb = new B;
  A* pa = pb;
  pa->f();          // OK, calls pa->B::f(7)
  pb->f();          // error: wrong number of arguments for B::f()
} — end examplefunction-definition: attribute-specifier-seq decl-specifier-seq declarator virt-specifier-seq function-body
function-body: ctor-initializer compound-statement function-try-block = default ; = delete ;
int max(int a, int b, int c) {
  int m = (a > b) ? a : b;
  return (m > c) ? m : c;
}
static const char __func__[] = "function-name";
had been provided, where function-name is an implementation-defined string.
struct S {
  S() : s(__func__) { }             // OK
  const char* s;
};
void f(const char* s = __func__);   // error: __func__ is undeclared
 — end exampleattribute-specifier-seq decl-specifier-seq declarator virt-specifier-seq = default ;
struct S {
  constexpr S() = default;              // ill-formed: implicit S() is not constexpr
  S(int a = 0) = default;               // ill-formed: default argument
  void operator=(const S&) = default;   // ill-formed: non-matching return type
  ~S() noexcept(false) = default;       // deleted: exception specification does not match
private:
  int i;
  S(S&);                                // OK: private copy constructor
};
S::S(S&) = default;                     // OK: defines copy constructor
 — end example
struct trivial {
  trivial() = default;
  trivial(const trivial&) = default;
  trivial(trivial&&) = default;
  trivial& operator=(const trivial&) = default;
  trivial& operator=(trivial&&) = default;
  ~trivial() = default;
};
struct nontrivial1 {
  nontrivial1();
};
nontrivial1::nontrivial1() = default;   // not first declaration
 — end exampleattribute-specifier-seq decl-specifier-seq declarator virt-specifier-seq = delete ;
struct onlydouble {
  onlydouble() = delete;                // OK, but redundant
  onlydouble(std::intmax_t) = delete;
  onlydouble(double);
};
struct sometype {
  void* operator new(std::size_t) = delete;
  void* operator new[](std::size_t) = delete;
};
sometype* p = new sometype;     // error, deleted class operator new
sometype* q = new sometype[3];  // error, deleted class operator new[]
 — end example
struct moveonly {
  moveonly() = default;
  moveonly(const moveonly&) = delete;
  moveonly(moveonly&&) = default;
  moveonly& operator=(const moveonly&) = delete;
  moveonly& operator=(moveonly&&) = default;
  ~moveonly() = default;
};
moveonly* p;
moveonly q(*p);                 // error, deleted copy constructor
struct sometype {
  sometype();
};
sometype::sometype() = delete;  // ill-formed; not first declaration
 — end exampleattribute-specifier-seq decl-specifier-seq ref-qualifier e initializer ;
auto f() -> int(&)[2]; auto [ x, y ] = f(); // x and y refer to elements in a copy of the array return value auto& [ xr, yr ] = f(); // xr and yr refer to elements in the array referred to by f's return value— end example
struct S { int x1 : 2; volatile double y1; };
S f();
const auto [ x, y ] = f(); initializer: brace-or-equal-initializer ( expression-list )
brace-or-equal-initializer: = initializer-clause braced-init-list
initializer-clause: assignment-expression braced-init-list
initializer-list: initializer-clause ... initializer-list , initializer-clause ...
braced-init-list: { initializer-list , } { }
expr-or-braced-init-list: expression braced-init-list
X a();
  int f(bool b) {
    unsigned char c;
    unsigned char d = c;        // OK, d has an indeterminate value
    int e = d;                  // undefined behavior
    return b ? d : 0;           // undefined behavior if b is true
  } — end example
int a;
struct X {
  static int a;
  static int b;
};
int X::a = 1;
int X::b = a;                   // X::b = X::a
 — end example
T x(a);
T x{a};
as well as in
new
expressions ([expr.new]),
static_cast
expressions ([expr.static.cast]),
functional notation type conversions ([expr.type.conv]),
mem-initializers ([class.base.init]), and
the braced-init-list form of a condition
is called
direct-initialization.
struct A {
  int x;
  struct B {
    int i;
    int j;
  } b;
} a = { 1, { 2, 3 } }; 
struct base1 { int b1, b2 = 42; };
struct base2 {
  base2() {
    b3 = 42;
  }
  int b3;
};
struct derived : base1, base2 {
  int d;
};
derived d1{{1, 2}, {}, 4};
derived d2{{}, {}, 4}; 
int x[] = { 1, 3, 5 };
struct S {
  int y[] = { 0 };          // error: non-static data member of incomplete type
}; — end example
struct A {
  int i;
  static int s;
  int j;
  int :17;
  int k;
} a = { 1, 2, 3 };
struct S { int a; const char* b; int c; int d = b[a]; };
S ss = { 1, "asdf" };
struct X { int i, j, k = 42; };
X a[] = { 1, 2, 3, 4, 5, 6 };
X b[2] = { { 1, 2, 3 }, { 4, 5, 6 } };
  struct A;
  extern A a;
  struct A {
    const A& a1 { A{a,a} };     // OK
    const A& a2 { A{} };        // error
  };
  A a{a,a};                     // OK
 — end example
struct S { } s;
struct A {
  S s1;
  int i1;
  S s2;
  int i2;
  S s3;
  int i3;
} a = {
  { },              // Required initialization
  0,
  s,                // Required initialization
  0
};                  // Initialization not required for A::s3 because A::i3 is also not initialized
 — end example
float y[4][3] = {
  { 1, 3, 5 },
  { 2, 4, 6 },
  { 3, 5, 7 },
};
float y[4][3] = {
  1, 3, 5, 2, 4, 6, 3, 5, 7
};
union u { int a; const char* b; };
u a = { 1 };
u b = a;
u c = 1;                        // error
u d = { 0, "asdf" };            // error
u e = { "asdf" };               // error
 — end examplechar msg[] = "Syntax error on line %s\n";
int g(int) noexcept;
void f() {
  int i;
  int& r = i;                   // r refers to i
  r = 1;                        // the value of i becomes 1
  int* p = &r;                  // p points to i
  int& rr = r;                  // rr refers to what r refers to, that is, to i
  int (&rg)(int) = g;           // rg refers to the function g
  rg(i);                        // calls function g
  int a[3];
  int (&ra)[3] = a;             // ra refers to the array a
  ra[1] = i;                    // modifies a[1]
} — end exampledouble d = 2.0; double& rd = d; // rd refers to d const double& rcd = d; // rcd refers to d struct A { }; struct B : A { operator int&(); } b; A& ra = b; // ra refers to A subobject in b const A& rca = b; // rca refers to A subobject in b int& ir = B(); // ir refers to the result of B::operator int&— end example
double& rd2 = 2.0; // error: not an lvalue and reference not const int i = 2; double& rd3 = i; // error: type mismatch and reference not const— end example
struct A { };
struct B : A { } b;
extern B f();
const A& rca2 = f();                // bound to the A subobject of the B rvalue.
A&& rra = f();                      // same as above
struct X {
  operator B();
  operator int&();
} x;
const A& r = x;                     // bound to the A subobject of the result of the conversion
int i2 = 42;
int&& rri = static_cast<int&&>(i2); // bound directly to i2
B&& rrb = x;                        // bound directly to the result of operator B
 — end example
struct Banana { };
struct Enigma { operator const Banana(); };
struct Alaska { operator Banana&(); };
void enigmatic() {
  typedef const Banana ConstBanana;
  Banana &&banana1 = ConstBanana(); // ill-formed
  Banana &&banana2 = Enigma();      // ill-formed
  Banana &&banana3 = Alaska();      // ill-formed
}
const double& rcd2 = 2;         // rcd2 refers to temporary with value 2.0
double&& rrd = 2;               // rrd refers to temporary with value 2.0
const volatile int cvi = 1;
const int& r2 = cvi;            // error: cv-qualifier dropped
struct A { operator volatile int&(); } a;
const int& r3 = a;              // error: cv-qualifier dropped
                                // from result of conversion function
double d2 = 1.0;
double&& rrd2 = d2;             // error: initializer is lvalue of related type
struct X { operator int&(); };
int&& rri2 = X();               // error: result of conversion function is lvalue of related type
int i3 = 2;
double&& rrd3 = i3;             // rrd3 refers to temporary with value 2.0
 — end example
int a = {1};
std::complex<double> z{1,2};
new std::vector<std::string>{"once", "upon", "a", "time"};  // 4 string elements
f( {"Nicholas","Annemarie"} );  // pass list of two elements
return { "Norah" };             // return list of one element
int* e {};                      // initialization to zero / null pointer
x = double{1};                  // explicitly construct a double
std::map<std::string,int> anim = { {"bear",4}, {"cassowary",2}, {"tiger",7} }; — end example
double ad[] = { 1, 2.0 };           // OK
int ai[] = { 1, 2.0 };              // error: narrowing
struct S2 {
  int m1;
  double m2, m3;
};
S2 s21 = { 1, 2, 3.0 };             // OK
S2 s22 { 1.0, 2, 3 };               // error: narrowing
S2 s23 { };                         // OK: default to 0,0,0
 — end example
struct S {
  S(std::initializer_list<double>); // #1
  S(std::initializer_list<int>);    // #2
  S();                              // #3
  // ...
};
S s1 = { 1.0, 2.0, 3.0 };           // invoke #1
S s2 = { 1, 2, 3 };                 // invoke #2
S s3 = { };                         // invoke #3
 — end example
struct Map {
  Map(std::initializer_list<std::pair<std::string,int>>);
};
Map ship = {{"Sophie",14}, {"Surprise",28}}; — end example
struct S {
  // no initializer-list constructors
  S(int, double, double);           // #1
  S();                              // #2
  // ...
};
S s1 = { 1, 2, 3.0 };               // OK: invoke #1
S s2 { 1.0, 2, 3 };                 // error: narrowing
S s3 { };                           // OK: invoke #2
 — end example
enum byte : unsigned char { };
byte b { 42 };                      // OK
byte c = { 42 };                    // error
byte d = byte{ 42 };                // OK; same value as b
byte e { -1 };                      // error
struct A { byte b; };
A a1 = { { 42 } };                  // error
A a2 = { byte{ 42 } };              // OK
void f(byte);
f({ 42 });                          // error
enum class Handle : uint32_t { Invalid = 0 };
Handle h { 42 };                    // OK
 — end example
struct S {
  S(std::initializer_list<double>); // #1
  S(const std::string&);            // #2
  // ...
};
const S& r1 = { 1, 2, 3.0 };        // OK: invoke #1
const S& r2 { "Spinach" };          // OK: invoke #2
S& r3 = { 1, 2, 3 };                // error: initializer is not an lvalue
const int& i1 = { 1 };              // OK
const int& i2 = { 1.1 };            // error: narrowing
const int (&iar)[2] = { 1, 2 };     // OK: iar is bound to temporary array
 — end example
struct A { int i; int j; };
A a1 { 1, 2 };                      // aggregate initialization
A a2 { 1.2 };                       // error: narrowing
struct B {
  B(std::initializer_list<int>);
};
B b1 { 1, 2 };                      // creates initializer_list<int> and calls constructor
B b2 { 1, 2.0 };                    // error: narrowing
struct C {
  C(int i, double j);
};
C c1 = { 1, 2.2 };                  // calls constructor with arguments (1, 2.2)
C c2 = { 1.1, 2 };                  // error: narrowing
int j { 1 };                        // initialize to 1
int k { };                          // initialize to 0
 — end example
struct X {
  X(std::initializer_list<double> v);
};
X x{ 1,2,3 };
const double __a[3] = {double{1}, double{2}, double{3}};
X x(std::initializer_list<double>(__a, __a+3));
typedef std::complex<double> cmplx;
std::vector<cmplx> v1 = { 1, 2, 3 };
void f() {
  std::vector<cmplx> v2{ 1, 2, 3 };
  std::initializer_list<int> i3 = { 1, 2, 3 };
}
struct A {
  std::initializer_list<int> i4;
  A() : i4{ 1, 2, 3 } {}  // ill-formed, would create a dangling reference
};int x = 999; // x is not a constant expression const int y = 999; const int z = 99; char c1 = x; // OK, though it might narrow (in this case, it does narrow) char c2{x}; // error: might narrow char c3{y}; // error: narrows (assuming char is 8 bits) char c4{z}; // OK: no narrowing needed unsigned char uc1 = {5}; // OK: no narrowing needed unsigned char uc2 = {-1}; // error: narrows unsigned int ui1 = {-1}; // error: narrows signed int si1 = { (unsigned int)-1 }; // error: narrows int ii = {2.0}; // error: narrows float f1 { x }; // error: might narrow float f2 { 7 }; // OK: 7 can be exactly represented as a float int f(int); int a[] = { 2, f(2), f(2.0) }; // OK: the double-to-int conversion is not at the top level— end example