Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Removing the NCCE/CS pair regarding lambdas since that's not EXP61-CPP

...

Code Block
bgColor#ccccff
langcpp
int *g() {
  static int i = 12;
  return &i;
}
 
void h(int *i);
 
void f() {
  int *i = g();
  h(i);
}

Noncompliant Code Example

In this noncompliant code example, the function g() returns a lambda, which captures the automatic local variable i by reference. When that lambda is returned from the call, the reference it captured will refer to a variable whose lifetime has ended. As a result, when the lambda is executed in f(), the use of the dangling reference in the lambda results in undefined behavior. As a general rule, functions returning lambdas should not capture by reference. 

Code Block
bgColor#FFCCCC
langcpp
auto g() {
  int i = 12;
  return [&] {
    i = 100;
    return i;
  };
}

void f() {
  int i = g()();
}

Compliant Solution

In this compliant solution, the lambda does not capture i by reference but instead captures it by copy. Consequently, the lambda contains an implicit data member named i whose lifetime is that of the lambda.

Code Block
bgColor#ccccff
langcpp
auto g() {
  int i = 12;
  return [=] () mutable {
    i = 100;
    return i;
  };
}

void f() {
  int i = g()();
}

Noncompliant Code Example

std::initializer_list<> object is constructed from an initializer list as though the implementation allocated a temporary array and passed it to the std::initializer_list<> constructor. This temporary array has the same lifetime as other temporary objects except that initializing a std::initializer_list<> object from the array extends the lifetime of the array exactly like binding a reference to a temporary [ISO/IEC 14882-2014]. In this noncompliant code example, a member variable of type std::initializer_list<int> is list-initialized within the constructor's ctor-initializer. Under these circumstances, the conceptual temporary array's lifetime ends once the constructor exits, and so accessing any elements of the std::initializer_list<int> member variable results in undefined behavior.

Code Block
bgColor#FFCCCC
langcpp
#include <initializer_list>
#include <iostream>

class C {
  std::initializer_list<int> L;
  
public:
  C() : L{1, 2, 3} {}
  
  int first() const { return *L.begin(); }
};

void f() {
  C c;
  std::cout << c.first();
}

Compliant Solution

In this compliant solution, the std::initializer_list<int> member variable is replaced with a std::vector<int>, which copies the elements of the initializer list to the container instead of relying on a dangling reference to the temporary array:

Code Block
bgColor#ccccff
langcpp
#include <initializer_list>
#include <iostream>
#include <vector>
 
class C {
  std::vector<int> L;
  
public:
  C() : L{1, 2, 3} {}
  
  int first() const { return *L.begin(); }
};
 
void f() {
  C c;
  std::cout << c.first();
}

Noncompliant Code Example

In this noncompliant code example, a lamdba object is stored in a function object, which is later called (executing the lambda) to obtain a constant reference to a value. The lambda object returns an int value, which is then stored in a temporary int object that becomes bound to the const int & return type specified by the function object. However, the temporary object's lifetime is not extended past the return from the function object's invocation, which causes undefined behavior when the resulting value is accessed.

...

Code Block
bgColor#FFCCCC
langcpp
#include <functional>
 
void f() {
  auto l = [](const int &j) { return j; };
  std::function<const int&(const int &)> fn(l);
 
  int i = 42;
  int j = fn(i);
}

Compliant Solution

In this compliant solution, the std::function object returns an int instead of a const int &, ensuring that the value is copied instead of bound to a temporary reference. An alternative solution would be to call the lambda directly instead of through the std::function object.

Code Block
bgColor#ccccff
langcpp
#include <functional>
 
void f() {
  auto l = [](const int &j) { return j; };
  std::function<int(const int &)> fn(l);
 
  int i = 42;
  int j = fn(i);
}

Noncompliant Code Example

In this noncompliant code example, the constructor for the automatic variable s is not called because execution does not flow through the declaration of the local variable due to the goto statement. Because the constructor is not called, the lifetime for s has not begun. Therefore, calling S::f() uses the object outside of its lifetime and results in undefined behavior.

Code Block
bgColor#FFcccc
langcpp
class S {
  int V;
public:
  S() : V(12) {} // Not a trivial constructor
  void f();
};
 
void f() {
  // ...
 
  goto bad_idea;
 
  // ...
  S s; // Control passes over the declaration, so initialization does not take place.
 
bad_idea:
  s.f();
}

Compliant Solution

This compliant solution ensures that s is properly initialized prior to performing the local jump:

...