CS253: Software Development with C++

Spring 2023

Attributes

Show Lecture.Attributes as a slide show.

CS253 Attributes

AttributeValue
NameLogan
ClassCollege of Teaching Bard
AlignmentNeutral Good
Strength10
Intelligence18
Dexterity16
Sarcasm12
Wit18
Memory08

The attributes from my character sheet for this role-playing game.

Attributes

Attributes add extra information to a variable, function, type, or statement.

Attributes look like: [[attribute-name]] or [[attribute-name("reason")]]

[[deprecated]]

[[deprecated]]: discourage the use of a variable or function, with an optional reason given. “Deprecated” means support will be withdrawn soon—stop using it.

[[deprecated]] int kansas() { return 35; }

[[deprecated("one-star rating")]] int texas = 28;

int main() {
    return kansas() + texas;
}
c.cc:6: warning: ‘int kansas()’ is deprecated
c.cc:1: note: declared here
c.cc:6: warning: ‘texas’ is deprecated: one-star rating
c.cc:3: note: declared here

Deprecation examples

// Compiled under C++ 2014
bool printed = false;
if (!printed++)  // 🦡
    cout << "Bill";
c.cc:3: warning: use of an operand of type ‘bool’ in ‘operator++’ is 
   deprecated
Bill
// Compiled under C++ 2017
bool printed = false;
if (!printed++)  // 🦡
    cout << "Ted";
c.cc:3: error: use of an operand of type ‘bool’ in ‘operator++’ is 
   forbidden in C++17

In the bad old days, we incremented bool vars to make ’em true. Postincrement was a cheap way to test the old value while setting it to true.

Deprecation examples

[[fallthrough]]: I meant to do that!

int n = 0;
switch (n) {
  case 0:
    cout << "0\n";
    [[fallthrough]];
  case 2:
    cout << "even\n";
    break;
  case 1:
    cout << "1\n";  // 🦡
  case 3:
    cout << "odd\n";
}
c.cc:10: warning: this statement may fall through
c.cc:11: note: here
0
even

[[maybe_unused]]

[[maybe_unused]]: don’t complain that a type, variable, argument, or function isn’t being used.

int main(int argc, char *argv[]) {
    // argc, shmargc!
    cout << "program name: " << argv[0];
    return 0;
}
c.cc:1: warning: unused parameter ‘argc’
program name: ./a.out

https://wikipedia.org/wiki/Shm-reduplication

int main([[maybe_unused]] int argc, char *argv[]) {
    cout << "program name: " << argv[0] << '\n';
    return 0;
}
program name: ./a.out

Alternatives to [[maybe_unused]]

In this case, it would have been better to simply omit the name of the argument:

int main(int, char *argv[]) {
    cout << "program name: " << argv[0] << '\n';
    return 0;
}
program name: ./a.out

or, if the name has mnemonic value, place it in a /* comment */:

int main(int /* argc */, char *argv[]) {
    cout << "program name: " << argv[0] << '\n';
    return 0;
}
program name: ./a.out

[[nodiscard]]

[[nodiscard]]: the return value must be used.

int foo() {
    return 1;
}

[[nodiscard]] int bar() {
    return 2;
}

int main() {
    foo();
    bar();
}
c.cc:11: warning: ignoring return value of ‘int bar()’, declared with 
   attribute ‘nodiscard’
c.cc:5: note: declared here

This isn’t the default, because many functions do something and also return an error indication or status. Ignoring the return value is ok, in certain contexts.

[[nodiscard]]

Novices confuse vector::clear() and vector::empty().

As of C++20, vector::empty() is marked [[nodiscard]], so incorrect use is flagged:

vector v = {11,22,33};
v.empty();  // 🦡 clear values (Oops!)
cout << v.size() << '\n';
c.cc:2: warning: ignoring return value of ‘bool std::vector<_Tp, 
   _Alloc>::empty() const [with _Tp = int; _Alloc = std::allocator<int>]’, 
   declared with attribute ‘nodiscard’
In file included from /usr/local/gcc/11.2.0/include/c++/11.2.0/vector:67,
                 from /usr/local/gcc/11.2.0/include/c++/11.2.0/functional:62,
                 from /usr/local/gcc/11.2.0/include/c++/11.2.0/pstl/glue_algorithm_defs.h:13,
                 from /usr/local/gcc/11.2.0/include/c++/11.2.0/algorithm:74,
                 from /usr/local/gcc/11.2.0/include/c++/11.2.0/x86_64-pc-linux-gnu/bits/stdc++.h:65,
                 from /s/bach/a/class/cs000/public_html/pmwiki/cookbook/c++-includes.h:7,
                 from <command-line>:
/usr/local/gcc/11.2.0/include/c++/11.2.0/bits/stl_vector.h:1007: note: declared 
   here
3

[[nodiscard]] failure

It would be swell if [[nodiscard]] caught this, but it doesn’t.

class Point {
  public:
    Point(int a, int b) : x(a), y(b) { };
    int x, y;
};

Point(3,5); // Create & discard an unnamed Point

It would help those who don’t understand C++ constructor delegation.

[[noreturn]]

void depart() { exit(1); }

int foo() {
    if (getpid() == 0)  // no root!
        depart();
    else
        return 4;
}

int main() {
    cout << "Hello, world!\n";
    return foo();
}
c.cc:5: warning: control reaches end of non-void function
Hello, world!

The compiler is concerned that, after depart() returns, foo() will have no return value. We know that depart() won’t return, but the compiler couldn’t know that if depart() were in a separate source file.

[[noreturn]]

[[noreturn]] indicates that a function does not return.

[[noreturn]] void depart() { exit(1); }

int foo() {
    if (getpid() == 0)  // no root!
        depart();
    else
        return 4;
}

int main() {
    cout << "Hello, world!\n";
    return foo();
}
Hello, world!

Now, the compiler is happy.