File: uniforminit.yo

package info (click to toggle)
c%2B%2B-annotations 12.2.0-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 13,044 kB
  • sloc: cpp: 24,337; makefile: 1,517; ansic: 165; sh: 121; perl: 90
file content (140 lines) | stat: -rw-r--r-- 5,750 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
When defining variables and objects they may immediately be given
initial values. Class type objects are always initialized using one of their
available constructors. bf(C) already supports the array and struct
 hi(initialization)emi(initializer list) consisting of a list of constant
expressions surrounded by a pair of curly braces.

bf(C++) supports a comparable initialization, called 
    emi(uniform initialization). It uses the following syntax:
        verb(    Type object{ value list };)

When defining objects using a list of objects each individual object
may use its own uniform initialization.

The advantage of uniform initialization over using constructors is that using
constructor arguments may sometimes result in an ambiguity as constructing an
object may sometimes be confused with using the object's overloaded function
call operator (cf. section ref(FUNOBJ)). As initializer lists can only be used
with em(plain old data) (POD) types (cf. section ref(POD)) and with classes
that are `initializer list aware' (like tt(std::vector)) the ambiguity does
not arise when initializer lists are used.

Uniform initialization can be used to initialize an object or
variable, but also to initialize data members in a constructor or implicitly
in the return statement of functions. Examples:
        verb(    class Person
    {
        // data members
        public:
            Person(std::string const &name, size_t mass)
            :
                d_name {name},
                d_mass {mass}
            {}

            Person copy() const
            {
                return {d_name, d_mass};
            }
    };)

Object definitions may be encountered in unexpected places, easily resulting in
(human) confusion. Consider a function `tt(func)' and a very simple
class tt(Fun) (tt(struct) is used, as data hiding is not an issue here;
in-class implementations are used for brevity):
        verb(    void func();

    struct Fun
    {
        Fun(void (*f)())
        {
            std::cout << "Constructor\n";
        };

        void process()
        {
            std::cout << "process\n";
        }
    };)

Assume that in tt(main) a tt(Fun) object is defined as follows: 
        verb(    Fun fun(func);)

Running this program displays tt(Constructor), confirming that the
object tt(fun) is constructed.

Next we change this line of code, intending to call tt(process) from an
anonymous tt(Fun) object:
        verb(    Fun(func).process();)

As expected, tt(Constructor) appears, followed by the text tt(process).

    What about just defining an anonymous tt(Fun) object? We do:
        verb(    Fun(func);)

Now we're in for a surprise. The compiler complains that tt(Fun)'s default
constructor is missing. Why's that? Insert some blanks immediately after
tt(Fun) and you get tt(Fun (func)). Parentheses around an identifier are OK,
and are stripped off once the parenthesized expression has been parsed. In
this case: tt((func)) equals tt(func), and so we have tt(Fun func): the
definition of a tt(Fun func) object, using tt(Fun)'s default constructor (which
isn't provided).

So why does tt(Fun(func).process()) compile? In this case we have a member
selector operator, whose left-hand operand must be an class-type object. The
object must exist, and tt(Fun(func)) represents that object. It's not the name
of an existing object, but a constructor expecting a function like tt(func)
exists. The compiler now creates an anonymous tt(Fun), passing it tt(func) as
its argument.

Clearly, in this example, parentheses cannot be used to create an anonymous
tt(Fun) object. However, the uniform initialization em(can) be used. To define
the anonymous tt(Fun) object use this syntax:
        verb(    Fun{ func };)

(which can also be used to immediately call one of its members. E.g.,
tt(Fun{func}.process())).


Although the uniform intialization syntax is slightly different from the
syntax  of an initializer list (the latter using the assignment operator) the
compiler nevertheless uses the initializer list if a constructor
supporting an initializer list is available. As an example consider:
        verb(    class Vector
    {
        public:
            Vector(size_t size);
            Vector(std::initializer_list<int> const &values);
    };

    Vector vi = {4};)

When defining tt(vi) the constructor expecting the initializer list is
called rather than the constructor expecting a tt(size_t) argument. If the
latter constructor is required the definition using the standard constructor
syntax must be used. I.e., tt(Vector vi(4)).

    Initializer lists are themselves objects that may be constructed using
another initializer list. However, values stored in an initializer list are
immutable. Once the initializer list has been defined their values remain
as-is.

Before using initializer lists the
    tt(initializer_list)hi(initializer list) header file must be included.

Initializer lists support a basic set of member functions and constructors:
    itemization(
    ithtq(initializer_list)(initializer_list<Type> object)
        (defines tt(object) as an empty initializer list)
    ittq(initializer_list<Type> object { list of Type values })
        (defines tt(object) as an initializer list containing tt(Type) values)
    ittq(initializer_list<Type> object(other))
        (initializes tt(object) using the values stored in tt(other))
    ithtq(size)(size_t size() const)
        (returns the number of elements in the initializer list)
    ithtq(begin)(Type const *begin() const)
        (returns a pointer to the first element of the initializer list)
    ithtq(end)(Type const *end() const)
        (returns a pointer just beyond the location of the last element of the
initializer list)
    )