T D
T D
( D1 )the type of the contained declarator-id is the same as that of the contained declarator-id in the declaration
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 D1and the type of the identifier in the declaration T D1 is “derived-declarator-type-list T”, then the type of the identifier of D is “derived-declarator-type-list cv-qualifier-seq pointer to T”.
const int ci = 10, *pc = &ci, *const cpc = pc, **ppc; int i, *p, *const cp = &i;declare ci, a constant integer; pc, a pointer to a constant integer; cpc, a constant pointer to a constant integer; ppc, a pointer to a pointer to a constant integer; i, an integer; p, a pointer to integer; and cp, a constant pointer to integer.
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; // errorEach is unacceptable because it would either change the value of an object declared const or allow it to be changed through a cv-unqualified pointer later, for example:
*ppc = &ci; // OK, but would make p point to ci because of previous error *p = 5; // clobber ci
& attribute-specifier-seq D1 && attribute-specifier-seq D1and the type of the identifier in the declaration T D1 is “derived-declarator-type-list T”, then the type of the identifier of D is “derived-declarator-type-list reference to T”.
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&
nested-name-specifier * attribute-specifier-seq cv-qualifier-seq D1and the nested-name-specifier denotes a class, and the type of the identifier in the declaration T D1 is “derived-declarator-type-list T”, then the type of the identifier of D is “derived-declarator-type-list cv-qualifier-seq pointer to member of class nested-name-specifier of type T”.
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;declares pmi, pmf, pmd and pmc to be a pointer to a member of X of type int, a pointer to a member of X of type void(int), a pointer to a member of X of type double and a pointer to a member of Y of type char respectively.
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-seqand the type of the identifier in the declaration T D1 is “derived-declarator-type-list T”, then the type of the identifier of D is an array type; if the type of the identifier of D contains the auto type-specifier, the program is ill-formed.
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”
float fa[17], *afp[17];declares an array of float numbers and an array of pointers to float numbers.
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-typeand the type of the contained declarator-id in the declaration T D1 is “derived-declarator-type-list T”, T shall be the single type-specifier auto.
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-clauseThe optional attribute-specifier-seq in a parameter-declaration appertains to the parameter.
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
typedef void F();
struct S {
const F f; // OK: equivalent to: void f();
};
typedef void F(); F fv; // OK: equivalent to void fv(); F fv { } // ill-formed void fv() { } // OK: definition of fv
int i, *pi, f(), *fpi(int), (*pif)(const char*, const char*), (*fpif(int))(int);declares an integer i, a pointer pi to an integer, a function f taking no arguments and returning an integer, a function fpi taking an integer argument and returning a pointer to an integer, a pointer pif to a function which takes two pointers to constant characters and returns an integer, a function fpif taking an integer argument and returning a pointer to a function that takes an integer argument and returns an integer.
typedef int IFUNC(int); IFUNC* fpif(int);
auto fpif(int)->int(*)(int);A trailing-return-type is most useful for a type that would be more complicated to specify before the declarator-id:
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);
template<typename... T> void f(T (* ...t)(int, int)); int add(int, int); float subtract(int, int); void g() { f(add, subtract); }
void 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) }
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)) } }
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
void f() { int i; extern void g(int x = i); // error extern void h(int x = sizeof(i)); // OK // ... }
int 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
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
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() }