File: noexcept.yo

package info (click to toggle)
c%2B%2B-annotations 13.02.02-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 13,576 kB
  • sloc: cpp: 25,297; makefile: 1,523; ansic: 165; sh: 126; perl: 90; fortran: 27
file content (119 lines) | stat: -rw-r--r-- 5,628 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
When throwing exceptions while trying to achieve the strong guarantee a
function's actions are usually split in two parts
    itemization(
    it() First, usually on a temporary object, all operations that may throw
are performed (which doesn't affect the target object)
    it() Then, the target object is modified using operations that offer the
em(nothrow) guarantee.
    )

    The actions in the first step might be made em(move aware) by using
tt(std::move) (e.g., to assign the source's values to a (possibly temporary)
destination). However, using tt(std::move) can easily affect the source (e.g.,
when extending the source's memory, moving the existing data to its new
locations), which breaks the first step's assumption, as the target object is
now modified. 

In this case (and generally) the move operation should not be allowed to throw
exceptions. This, in turn, implies that it is difficult to write code which
must offer a non-throwing moving constructor, if it uses (external) 
data types over which the moving constructor has no control. E.g.,
        verb(    template <typename Type>
    class MyClass
    {
        Type d_f;
        public:
            MyClass() = default;
            MyClass(MyClass &&tmp)
            :
                d_f(move(tmp.d_f))
            {}
    };)

Here, tt(MyClass)'s author has no control over the design of tt(Type). If
tt(MyClass) is instantiated with the type tt(Foreign), merely having a
(possibly throwing) copy constructor, then the following code breaks the
no-throw assumption underlying move constructors:
        verb(    MyClass<Foreign> s2{ move(MyClass<Foreign>()) };)

COMMENT(see examples/moving.cc)

If templates are able to detect whether tt(Type) has non-throwing move
constructors then their implementations may be optimized by
calling these move constructors (already modifying their targets in the first
part of code offering the strong guarantee) in situations where otherwise the
non-modifying, but more expensive copy constructor has to be used.

The ti(noexcept) keyword was introduced to allow such templates to perform
such optimizations. As with tt(throw) lists, checking for tt(noexcept) is
a run-time check, but the consequence of violating a tt(noexept) declaration
are more serious than violating a tt(throw) list: violating tt(noexcept)
results in calling tt(std::terminate), terminating the program, possibly
without unwinding the stack. In the context of the previous example, the
following code is flawlessly accepted by the compiler, demonstrating that
there is no compile-time checking of tt(noexcept):
        verb(    class Foreign
    {
        public:
            Foreign() = default;
            Foreign(Foreign const &other) noexcept
            {
                throw 1;
            }
    };)

However, when this class's copy constructor is called, execution aborts
with the following message:
        verb(    terminate called after throwing an instance of 'int'
    Abort)

Keep in mind that the current purpose of tt(noexcept) is to allow templates to
optimize their code by using move operations where the code must also be able
to offer the strong exception guarantee. Since tt(noexcept) also offers the
conditional tt(noexcept(condition)) syntax (with tt(noexcept(true)) and
tt(noexcept) having identical semantics), tt(noexcept) can be made
conditional to the `noexcepting' nature of template types. Note that this is
not possible with tt(throw) lists. 

The following hi(rule of thumb) rules of thumb may be used to decide whether
or not to use tt(noexcept) in your code:
    itemization(
    it() General rule: don't use tt(noexcept) (this is identical to the advise
        given for tt(throw) lists);
    it() Default implementations of constructors, copy- and move-assignment
        operators and destructors are provided with tt(noexcept(true)) if the
        compiler can deduce that composing types also offer
        tt(noexcept(true)), allowing template optimizations using move
        operations where possible.
    it() Functions provided with tt(noexcept) declarations may em(still) throw
        exceptions (see the example given above). In the end tt(noexcept)
        merely means that if such a function throws an exception
        tt(std::terminate) rather than tt(std::unexpected) is called. 
    it() Functions previously provided with an empty throw list (tt(throw()))
        should be provided with tt(noexcept).
    it() tt(noexcept) specifications are required when using the following std
        traits (declared in the tthi(type_traits) header file):
        itemization(
        iti(is_nothrow_constructible)
        iti(is_nothrow_default_constructible)
        iti(is_nothrow_move_constructible)
        iti(is_nothrow_copy_constructible)
        iti(is_nothrow_assignable)
        iti(is_nothrow_move_assignable)
        iti(is_nothrow_copy_assignable)
        iti(is_nothrow_destructible)
        )
        These type traits provide the member constant tt(value) which is
        tt(true) if the class (and possibly its argument type list) matches
        the characteristic after which the trait was named. E.g., if
        tt(MyClass(string const &) noexcept) is a constructor, then
        verb(    std::is_nothrow_constructible<MyClass, string>::value)

equals tt(true). For the named members (like
        tt(is_nothrow_move_constructible)) parameter types do not have to be
        specified, as they are implied. E.g.,
        verb(    std::is_nothrow_move_constructible<MyClass>::value)

returns tt(true) if the move constructor has the tt(noexcept)
        modifier. 
    )