01 Jan, 2014

Interlude

One year down the road, 2013 has gone by but not without modifications to the C++ lands. Two major compilers have reached C++11 conformance —GCC and Clang —. Shortly after, the Committee Draft (CD) for C++14 was completed, which is now just around the corner...

C++11

GCC became C++11 feature complete as of v4.8.1, and it started including experimental support of C++14 features —details here—.

Clang reached full C++11 conformance, language features and standard library features, as of v3.3, and it already is draft C++14 feature complete —details here—.

Visual C++ is running somewhat behind, but not being C++11 feature complete has not prevented it from supporting some of the upcoming C++14 features —details here—.

All things considered, it was an impressive year for C++ conformance. And notably, it looks like for the first time there will be feature complete compilers for a C++ standard on the very same year it is ratified. The following slide from GoingNative's Keynote: Herb Sutter - One C++ summarizes the situation nicely:

One C++

C++14

The C++14 Committee Draft was completed. Quoting from the announcement:

In August 2013, the primary comment international ballot (Committee Draft, aka “CD”) for C++14 was completed. Thanks to the high quality of the draft, the ballot results came in very clean: We received a total of 85 official comments (plus 30 unofficial late comments) from national bodies, which is far lower than than the over 500 comments received for each of the two comment ballots for C++0x/C++11.

At the September 2013 ballot resolution meeting, the committee generated final or tentative resolutions for all national body comments, and we expect all comment responses to be complete and applied at the upcoming February 2014 meeting, and for C++14 to enter its (potentially final) Draft International Standard (DIS) ballot following that meeting.

So what is new —tentatively, but highly unlikely to be removed at this point—?

What's new - Language Features

Lambda improvements

Lambdas as introduced in C++11 were monomorphic, as there were concerns on how polymorphic lambdas would interact with (heavyweight) concepts —which ended up not making the cut—. N3649 proposes to allow specifying lambdas parameters as auto, which follows the type inference semantics of a good old templated parameter —not those of auto—. The result is an unnamed function object with a templated function operator:

// 'Identity' is a lambda that accepts an argument of any type and
// returns the value of its parameter.  
auto Identity = [](auto a) { return a; };
int three = Identity(3);
char const* hello = Identity("hello");

// Conversion to function pointer for capture-less lambdas
int (*fpi)(int) = Identity;
char (*fpc)(char) = Identity;

Another shortcoming of C++11 lambdas was their inability to capture by move. Instead of introducing a new syntax for move capture, a generalized form of capture was proposed by N3648. This allows defining new names and/or values for the captures, consequentially allowing captures by move:

// Capture by move
auto ptr = std::make_unique<int>(10);
auto lambda = [ptr = std::move(ptr)] {return *ptr;};

// Synthesized captures
int x = 4;
auto y = [&r = x, x = x+1]()->int {
            r += 2;
            return x+2;
         }();  // Updates ::x to 6, and initializes y to 7.

Type Inference

N3638 proposes to extend the return type deduction of lambdas to functions, while at the same time relaxing the requirements allowing complex statements and multiple returns:

auto iterate(int len) // return type deduced int
{
  for (int i = 0; i < len; ++i)
    if (search (i))
      return i;
  return -1;
}

Additionally, it proposes decltype(auto) as a way of requiring decltype type inference semantics on a declaration:

int i;
int&& f();
auto           x3a = i;        // decltype(x3a) is int
decltype(auto) x3d = i;        // decltype(x3d) is int
auto           x4a = (i);      // decltype(x4a) is int
decltype(auto) x4d = (i);      // decltype(x4d) is int&
auto           x5a = f();      // decltype(x5a) is int
decltype(auto) x5d = f();      // decltype(x5d) is int&&
auto           x6a = { 1, 2 }; // decltype(x6a) is std::initializer_list<int>
decltype(auto) x6d = { 1, 2 }; // error, { 1, 2 } is not an expression
auto          *x7a = &i;       // decltype(x7a) is int*
decltype(auto)*x7d = &i;       // error, declared type is not plain decltype(auto)

Relaxed constexpr functions

A constexpr function in C++11 follows tight requirements, simplifying implementation while retaining functionality —mainly by allowing recursive calls, at the expense of expressiveness—. N3652 proposes to lift several of those requirements:

  • Allow declarations within constexpr functions, other than:
    • static or thread_local variables,
    • uninitialized variables;
  • Allow if and switch statements (but not goto),
  • Allow all looping statements: for (including range-based for), while, and do-while,
  • Allow mutation of objects whose lifetime began within the constant expression evaluation.

Additionally, it proposes to remove the rule that a constexpr non-static member function be implicitly const —which is a breaking change—:

constexpr int abs(int x) {      // OK
  if (x < 0)
    x = -x;
  return x;
}
constexpr int prev(int x) {     // OK
  return --x;
}
constexpr int g(int x, int n) { // OK
  int r = 1;
  while (--n > 0) r *= x;
  return r;
}

Syntax sugar

N3472 proposes binary literals as in the existing GCC extension —and is the same syntax as Java 7, Python, and D—: 0b1100.

N3651 proposes the creation of constexpr variables that are templated, for parameterized constants:

template <typename T>
constexpr T pi = T(3.1415926535897932385);

template <typename T>
T area_of_circle_with_radius(T r) {
  return pi<T> * r * r;
}

N3760 proposes the introduction of the [[deprecated]] attribute, providing a portable syntax to annotate entities whose use is discouraged.

N3781 proposes to use the single quotation mark ' as a digit separator: 1'048'576.

What's new - Standard Library Features

Compile-time integer sequences

N3658 proposes to add a type like template<int...> struct index_sequence { }; so that an instantiation can be passed to a function template that deduces a parameter pack containing the integer sequence 0, 1, 2 —more precisely, it proposes template<class T, T... I> struct integer_sequence, and index_sequence<size_t... I> as a template alias for integer_sequence<size_t, I...>—. With such a type in the standard library toolbox, it is easy to implement a non-recursive expansion of tuple elements:

template<class F, class Tuple, std::size_t... I>
decltype(auto) apply_impl(F&& f, Tuple&& t, index_sequence<I...>) {
  return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(t))...);
}

template<class F, class Tuple>
decltype(auto) apply(F&& f, Tuple&& t) {
  using Indices = make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>;
  return apply_impl(std::forward<F>(f), std::forward<Tuple>(t), Indices());
}

Quoted Strings

N3654 proposes to add the quoted stream I/O manipulator, which places delimiters —defaulted to double-quote "— around strings on output, and strips off the delimiters on input. This ensures strings with embedded white space round-trip as desired:

std::stringstream ss;
std::string original = "foolish me";
std::string round_trip;

ss << quoted(original);
ss >> quoted(round_trip);

std::cout << original;     // outputs: foolish me
std::cout << round_trip;   // outputs: foolish me

assert(original == round_trip); // assert will not fire

make_unique

C++11 provided make_shared for shared_ptr, but not make_unique for unique_ptr; this is widely viewed as an oversight. N3656 proposes the adoption of make_unique, with which is now possible to say "never say new/delete" without caveats:

auto ptr = std::make_unique<int>(10);

Shared Locking in C++

N3659 proposes to add functionality to allow clients to easily code the well-known multiple-readers/single-writer locking pattern:

std::shared_mutex mut;

void example_reader()
{
  std::shared_lock<std::shared_mutex> _(mut);
  / mut is now shared-locked
  // ...
}   // mut is now unlocked

void example_writer()
{
  std::scoped_lock<std::shared_mutex> _(mut);
  // mut is now unique-locked
  // ...
}   // mut is now unlocked

This proposal was originally part of the Multithreading API for C++0X proposal, which was excluded to limit the scope of C++0X in hopes of shipping a new standard by 2009.

std::result_of and SFINAE

Computing the result type of an expression can easily be done with std::result_of, as opposed to writing a longer type computation involving decltype and std::declval. However, C++11 requires the resulting INVOKE expression within result_of to be well-formed, while using decltype directly does not cause a hard error. When used in function signatures, the direct use of decltype of an ill-formed call expression causes the function to be dropped due to SFINAE. N3462 proposes to condition the presence of the nested result_of<>::type typedef on whether the call expression is ill-formed or not, thus making it SFINAE-friendly:

struct eat { template<typename T> eat(T const &) {} };
struct not_incrementable {};

struct inc {
  template<typename T>
  auto operator()(T t) const -> decltype(t++)
  { return t++; }
};

template<typename A>
typename std::result_of<inc(A)>::type
try_inc(A a) {
  return inc()(a);
}

not_incrementable try_inc(eat) {
  return not_incrementable();
}

int main() {
  int x = try_inc(1); // OK
  not_incrementable y = try_inc(std::string("foo")); // OK, not_incrementable
}

Making Operator Functors greater<>

N3421 proposes to make <functional>'s operator function objects easier to use and more generic. It does so by introducing the greater<> syntax —where the template argument defaults to void— resulting in a function object with a templated operator(), capable of accepting and perfectly forwarding arbitrary argument types and its result. This is referred to as transparent operators:

std::sort(v.begin(), v.end(), std::greater<ValueType>()); // C++11
std::sort(v.begin(), v.end(), std::greater<>()); // this proposal

Adding heterogeneous comparison lookup to associative containers

The associative container lookup functions only take an argument of key_type, resulting in an implicit or explicit creation of a key object to do the lookup. N3657 proposes to allow searches using types which are comparable with the key_type in the cases where the comparison object is transparentkey_compare::is_transparent exists—. The following variation constructs no temporary std::string objects:

std::set<std::string, std::less<>> s = /* ... */;
s.find("key");

Addressing Tuples by Type

N3670 proposes to allow accessing tuple fields by type:

std::tuple<std::string, std::string, int> t("foo", "bar", 7);
int i = std::get<int>(t); // i == 7
int j = std::get<2>(t); // Equivalent to the above: j == 7
string s = std::get<std::string>(t); // Compile-time error. Ambiguous.

User-defined Literals

The standard library is lacking predefined user-defined literals, even though the standard reserves names not starting with an underscore for it. N3642 proposes to add user-defined literal for std::strings—, and several others for chrono durations —h, min, s, ms, us, ns—. N3660 complements it by proposing user-defined literals for std::complexi, il and if for std::complex<double>, std::complex<long double> and std::complex<float> respectively—.

TransformationTraits Redux

C++11 introduced a number of TransformationTraits —a class template that takes one template type argument and optional arguments that help define a transformation, and defines a nested type named type as a synonym for the transformed type—. N3655 proposes to add a set of template aliases for the TransformationTraits present in the library in order to reduce the noise caused by the sorrounding typename and ::type. Those template aliases take the number of the base TransformationTrait with a _t prefix:

template<class T>
using reference_t = std::conditional_t<std::is_reference<T>::value, T, std::add_lvalue_reference_t<T>>;

That's it?

This is by no means an exhaustive list! The new standard also includes a number of defect fixes, improvements and minor clarifications. Furthermore, a considerable amount of work is heading for a separate Technical Specification (TS), including those things that didn't make it in time for C++14:

  • File System, Beman Dawes: Work based on Boost.Filesystem v3, including file and directory iteration.

  • Networking, Robert Pratte: A small set of network-related libraries including support for network byte order transformation and URIs.

  • Concepts Lite, Andrew Sutton: Language extensions for template type checking.

  • Library Fundamentals, Jeffrey Yasskin: A set of standard library extensions for vocabulary types like std::optional<> and other fundamental utilities.

  • Array Extensions, Lawrence Crowl: Language and library extensions related to arrays, including runtime-sized arrays (aka arrays of runtime bound) and std::dynarray<>.

  • Extensions for Concurrency, Artur Laksberg: Initially includes library support for executors and non-blocking extensions to std::future. Additionally may include language extensions like await, and additional libraries such as concurrent hash containers and latches.

  • Extensions for Parallelism, Jared Hoberock: Initially includes a Parallel STL library with support for parallel algorithms to exploit multiple cores, and vectorizable algorithms to exploit CPU and other vector units.

The C++ lands grew bigger during 2013, and we can only expect this trend to intensify during 2014. With the likely ratification of C++14 and at least some of the TS, it will certainly be a good year for C++...


This blog runs on Nibbleblog, a powerful engine for creation and manipulation of blogs; it's generated by Markdown, a powerful yet clean text-to-HTML conversion tool; and uses Syntax Highlighter, a fully functional self-contained code syntax highlighter, for its code snippets.