ISO C++ Cologne Trip Report

Erich Keane <erich.keane@intel.com>

Core (Complete)

P1163: Deprecate use of comma in subscript expressions

  • Comma operator in array subscript is horribly uncommon.
  • Blocks ability to do a sensible mdspan, non-jagged arrays.
// Before:
array[x]     // Ok
array[(x,y)] // Ok, uses y as index/key
// Ok, uses y as index/key
array[x,y]   
// After:
array[x]      // Ok
array[(x,y)]  // Ok, uses y as index/key
// Deprecated, uses y as index/key
array[x,y]    
// Someday we get:

mdspan<int, array_property::dimension<2, 3, 5>> foo(/*...*/);
int value = foo[1, 2, 3];

P1331: Permitting trivial default initialization in constexpr contexts

  •   Permit default initialization for trivially default constructible types in constexpr.  In short: so long as uninitialized values are not read from, permit in constexpr.
  • UB is still not allowed in constexpr, instead of failing at init time, fail at uninitialized read.
// Already Allowed.
template <typename T>
constexpr T f1(const T& other) {
  auto* t = new T;  // default init
  *t = other;
  T out = *t;
  delete t;
  return out;
}
template <typename T> 
constexpr T f2(const T& other) {
  T t; // default init 
  t = other;  
  T out = t;
  return out;
}
// Allow f2
template <typename T>
constexpr T f1(const T& other) {
  auto* t = new T[1];  // default init
  t[0] = other;
  T out = t[0];
  delete[] t;
  return out;
}
template <typename T> 
constexpr T f2(const T& other) {
  T t[1]; // Previously fails.
  t[0] = other;  
  T out = t[0];
  return out;
}

P0735: Interaction of memory_order_consume with release sequences

  • The current definition of memory_order_consume is not sufficient to allow implementations to map a consume load to a "plain" load instruction (i.e. without using fences), despite this being the intention of the original proposal. Instead, memory_order_consume is typically treated as though the program had specified memory_order_acquire, which is now preferred by the standard

P08048: Conditionally Trivial Special Member Functions

  • Change trivial special member functions to no longer require ALL versions be trivial, just the 'active' one (not deleted, not SFINAE disabled, etc).
// Make this type be trivially copy constructible
// if T is (previously was not).
template <typename T>
struct optional {
    optional(optional const&)
        requires TriviallyCopyConstructible<T> 
                 && CopyConstructible<T>
        = default;
    optional(optional const& rhs)
            requires CopyConstructible<T>
       : engaged(rhs.engaged)
    {
        if (engaged) {
            new (value) T(rhs.value);
        }
    }
};

Spaceship Fixes:

  • P1186: When do you actually use <=>?
    • Some fixes regarding the synthesis of the three-way-compare operator.
  • P1630: Spaceship needs a tune-up.

    • Cleanup some interaction between operator==, operator!= and operator<=>

P1301: [[nodiscard("should have a reason")]]

  • nodiscard attribute now takes an optional argument that permits the programmer to specify the 'reason'.
struct data_holder {
private:
	std::unique_ptr<char[], data_deleter> ptr;
	std::vector<int> indices;
	// ...
public:
	[[nodiscard("Possible memory leak.")]] 
	char* release() { indices.clear(); return ptr.release(); }

	void clear() { indices.clear(); ptr.reset(nullptr); }

	[[nodiscard("Did you mean 'clear'?")]] 
	bool empty() const { return ptr != nullptr && !indices.empty(); }
};
int main () {
	data_holder dh = /* ... */;
	// serious error, with explanation
	dh.release();
	// less serious, with explanation
	dh.empty();
	return 0;
}

P1099: Using Enum

  • Introduces a new 'using' statement to introduce the names of a scoped enum into the scope.
// Before
std::string_view 
to_string(rgba_color_channel channel) {
  switch (channel) {
    case rgba_color_channel::red:   
    return "red";
    case rgba_color_channel::green:
    return "green";
    case rgba_color_channel::blue:
    return "blue";
    case rgba_color_channel::alpha:
    return "alpha";
  }
}
// After
std::string_view 
to_string(rgba_color_channel channel) {
  switch (my_channel) {
    using enum rgba_color_channel;
    case red:   
    return "red";
    case green: 
    return "green";
    case blue:  
    return "blue";
    case alpha: 
    return "alpha";
  }
  
  // Also allowed:
  using rgba_color_channel::red;
  auto x = red;
}

P1616: Using unconstrained template template parameters with constrained templates

  • Unconstrained template template parameters cannot accept constrained templates.  Let them.
// Given:
template<class T, class U> 
struct Unconstrained { Unconstrained(U, V);};
template<class T, class U, class = enable_if_t<is_integral_v<U>>> 
struct Sfinae { Sfinae(U, V); };
template<class T, class U> requires Integral<U> 
struct Conceptized { Conceptized(U, V); };
// Before:
auto u4 = make_unique<Unconstrained>(3, 5); // OK. unique_ptr<Unconstrained<int, int>>
auto s4 = make_unique<Sfinae>(3, 5);        // OK. unique_ptr<Unconstrained<int, int>>
// Ill-formed instead of desired Conceptized<int, int>
auto c4 = make_unique<Conceptized>(3, 5);   

// Make this work:
auto c4 = make_unique<Conceptized>(3, 5);  // Proposed: Conceptized<int, int>

P1816: wording for class template argument deduction for aggregates (P1021).

  • Let CTAD work for aggregates:
template<class T>
struct Point { T x; T y; };
 
// Before:
// Aggregate: Cannot deduce
Point<double> p{3.0, 4.0};
Point<double> p2{.x = 3.0, .y = 4.0};
 
// After:
// Proposed: Aggregates deduce
Point p{3.0, 4.0};
Point p2{.x = 3.0, .y = 4.0};

P1668: Enabling constexpr intrinsics by permitting unevaluated inline-assembly in constexpr functions.

  • Unevaluated inline-assembly should be OK, allows intrinsics to be constexpr without a second function.
 constexpr double fma(double b, double c, double d) {
  if (std::is_constant_evaluated())
   return b * c + d; // <ed note>I know, this is wrong.
  asm("vfmadd132sd %0 %1 %2"
   : "+x"(b)
   : "x" (c), "x" (d)
   );
  return b;
 }

Modules Fixes:

  • P1766: Mitigating minor modules maladies
    • Fixes for 3 minor modules issues when it comes to linkage.
  • P1811: Relaxing redefinition restrictions for re-exportation robustness.
    • Relaxes redefinition of class rules in modules, so that multiple #includes without guards don't break.

P0388: Permit conversions to arrays of unknown bounds.

  • As of CWG393 function parameters can be pointers or references to arrays of unknown bound. However, binding such a parameter to an array of known bound isn't permitted:
void f(int(&)[]);
int arr[1];

f(arr);          // Error
int(&r)[] = arr; // Error
// Paper allows:
void f(int    (&&)[] );    // #1
void f(double (&&)[] );    // #2
void f(int    (&&)[2]);    // #3

f( {1} );       // Calls #1: Better than #2 due to conversion, better than #3 due to bounds
f( {1.0} );     // Calls #2: Identity conversion is better than floating-integral conversion
f( {1.0, 2.0} );// Calls #2: Identity conversion is better than floating-integral conversion
f( {1, 2} );    // Calls #3: Converting to array of known bound is better than to unknown
                //           bound, and an identity conversion is better than
                //           floating-integral conversion

P1823: Remove Contracts from C++20

  • Tons of discussions, motivation:
  • We had major design changes proposed and accepted by EWG this meeting (Cologne, July 2019)
    • We have significant disagreement whether these changes make things better or worse. In fact, even the initial authors of Contracts for C++20 have no agreement.
    • We still discuss what the design changes mean.
    • We still design details on the fly.
    • Some major concerns were raised regarding the proposed changes.
    • We have no implementation experience (such as applying the currently proposed feature in the standard library)

P1143: Adding the constinit keyword.

  • Add constinit keyword, goes where constexpr goes.
  • Only valid to static or thread-storage variable declarations.
  • Doesn't permit dynamic initialization, initialization must be constexpr.

P1452: On the non-uniform semantics of return-type-requirements

  • Trailing "->Type" and "->Concept" were inconsistent.
  • Blocks future direction of "Concept auto".
  • Remove ->Type form of return-type-requirement, all must be a Concept now:
template<typename T>
concept R = requires (T i) {
    typename T::type;
    // No longer allowed.
    {*i} -> const typename T::type&;
    // Switch to:
    {*i} -> ConvertibleTo<const typename T::type&>;
    // OR:
    {*i} -> Same<const typename T::type&>;
  };

P1152: Deprecating volatile

  • Too many to list, but deprecates most uses of volatile.
  • Increment/decrement of a volatile.
  • Compound Assignment of a volatile.
  • Volatile parameters/return types.
  • Volatile Structured Bindings.

P1771: [[nodiscard]] for constructors.

  • Does a good job fixing the accidental lock_guard temporary problem.
void foo() {
  // Immediately locks then unlocks, no diagnostic.
  std::lock_guard<Mutex>(SomeMutex);
  ...
}

// Change lock_guard ctor to:
template<typename T> class lock_guard {
  ...
  [[nodiscard]]
  lock_guard(T &){...}
  ...
}

void foo() {
  // Now warns, you meant to give this a name!
  std::lock_guard<Mutex>(SomeMutex);
  ...
}

P1825: Merged wording for P0527R1 and P1155R3

  • P0527R1: Implicit move from rvalue references in return statements
  • P1155R3: More implicit moves
  • throws/returns now will move if possible.
  • May be an error if there is missing move constructor.

 

P1703: Recognizing Header Unit Imports Requires Full Preprocessing.

  • Currently, recognizing header unit imports requires full preprocessing which is problematic for dependency scanning and partial preprocessing. This paper proposes changes that will allow handling such imports with the same degree of preprocessing as #include directives.

  • Import declarations of a header must:

    • Start with "import" or "export import" tokens, not produced by a macro replacement.

    • Followed, after macro replacement, by header-name-tokens.

    • The entire, single, and only declaration is on one line.

  • Note: Non-header version can still combine lines.

 

P0784: More constexpr containers.

  • constexpr's allocator traits and the default allocator.

  • Relaxes the rules to permit non-transient allocations in constexpr time.

  • Should enable a bunch of STL containers to be used at constexpr time!

 

Library Highlights:

P0533: Bit operations, P0631: Math Constants

  • P0533: Adds to <bit> (all constexpr):
    • rotl/rotr
    • count leading and trailing 1s or zeros.
    • popcount.
  • P0631: Adds a bunch of constants to <numbers>
    • pi, inverse pi, inverse sqrt pi
    • log2e, log10e, ln2, ln10
    • sqrt2, sqrt3
    • inverse sqrt3
    • egamma
    • phi

P0645: Text Formatting

  • Alternative to ostreams, provides a checked-format-like string!
  • Does a lot of the things that printf does.
  • P1361: Adds std::chrono support
  • P1652: some additional fixes.
string message = format("The answer is {}.", 42);

time_t t = time(nullptr);
string date = format("The date is {0:%Y-%m-%d}.", *localtime(&t));
// ***********centered***********
format("{:*^30}", "centered");

P1754: Rename concepts to standard_case for C++20 while we still can.

  • Concepts are now named with the standard_case instead of PascalCase
  • Some changed, Same->same_as
  •  

P1614: The Mothership has landed.

  • Adds spaceship throughout the STL.

P1004: Making std::vector constexpr, P0980: Making std::string constexpr

  • Make the two types valid in constexpr!

P0660:Stop Token and Joining Thread

  • Creates a jthread type that automatically signales a request to stop the thread, then joins it (if possible).
  • Allows request_stop to request a stop.
  • Adds some stop_callback stuff.

P1208: Adopt source_location for C++20

  • std::experimental::source_location now added to the <source_location> header, and is C++20!

Cologne Trip Report

By Erich Keane

Cologne Trip Report

  • 532