CS253: Software Development with C++

Fall 2021

Attributes

Show Lecture.Attributes as a slide show.

CS253 Attributes

AttributeValue
NameJack
ClassTeacher
Alignment  Lawful Good
Strength10
Intelligence12
Dexterity6
Sarcasm17
Vaccination18

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 foo() { return 1; }

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

int main() {
    return foo() + texas;
}
c.cc:7: warning: 'int foo()' is deprecated
c.cc:1: note: declared here
c.cc:7: warning: 'int foo()' is deprecated
c.cc:1: note: declared here
c.cc:7: warning: 'texas' is deprecated: one-star rating
c.cc:4: 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:6: warning: ignoring return value of 'int bar()', declared with attribute 
   nodiscard
c.cc:2: 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<int> v = {11,22,33};
v.empty();  // clear values (Oops!)
cout << v.size() << '\n';
3

Once we get a C++20 compiler on this web server, you’ll see it.

[[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() { throw "Goodbye"; }

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

int main() {
    cout << "Hello, world!\n";
    return foo();
}
c.cc:8: 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() { throw "Goodbye"; }

int foo() {
    if (rand() < 100)
        depart();
    else
        return 4;
}

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

Now, the compiler is happy.