License : Creative Commons Attribution 4.0 International (CC BY-NC-SA 4.0)
Copyright : Hervé Frezza-Buet, CentraleSupelec
Last modified : April 19, 2024 10:22
Link to the source : syntax.md

Table of contents

Syntactic sugar in C++

Structured bindings

#include <tuple>
#include <utility>
#include <string>
#include <algorithm>

std::pair<int, int> sq(int x) {return {x, x*x};}

auto weard(int x) {return std::make_tuple(x, std::to_string(x), 'c');}

std::pair<int&,int&> head_tail(std::vector<int>& vec) {return {*(vec.begin()), *(vec.end()-1)};}

int main() {
  std::vector<int> v = {1, 4, 5, 7, 9, 11};

  auto  [a, a_2 ] = sq(5);
  auto  [x, y, z] = weard(10);
  auto& [hd, tl ] = head_tail(v);

  hd = 36; // changes the vector's first element.

  // More usefull
  auto [min_iter, max_iter] = std::minmax_element(v.begin(), v.end());
}

The STL type std::tie can be used harmoniously with bindings.

#include <tuple>
#include <iostream>

std::tuple<double, double, double> surf(double u, double v) {return {u, v, u*u+v*v};}

int main() {
  auto [x1, y1, z1] = surf(1,2);

  double x2, y2, z2;
  std::tie(x2, y2, z2) = surf(1,3);

  // The lexicographic ordering is implicit.
  if(std::tie(x1, y1, z1) < std::tie(x2, y2, z2))
    std::cout << "Inf" << std::endl;
  
  return 0;
}

if and switch statement

std::map<std::string,int> name_age;
...
if(auto [it, inserted] = name_age.insert({"John", 43}) ; inserted) { ... }

switch(int a = f(....); a) {
  case '3': ...; break;
  default : std::cout << a << "is an unhandeld case" << std::endl; break;
}

The for loop with iterators

The basic for loop with iterators is:

for(auto it = collection.begin(); it != collection.end(); ++it) {
  auto& elem = *it; // or auto, const auto&, or the type of the element...
  do_something_with(elem);
}

It can be summarized as:

for(auto& elem : collection) {
  do_something_with(elem);
}

Aggregate initialization

The aggregate initialization simplifies and unifies initializations.

#include <iostream>
#include <string>

struct Dude {
  std::string name;
  int age;
  // Warning, no constructors are defined here...
};

int main(int argc, char* argv[]) {
  // .... so the default brace initialization works.
  Dude patrick    {std::string("Patrick"), 50};
  Dude pierre   = {std::string("Pierre"),  42};
 
  return 0;
}
#include <iostream>
#include <string>
#include <initializer_list>

// We define constructors, only the ones that we have defined can be
// used for brace initialization.
struct Dude {
  std::string name;
  int age;
  
  Dude(const std::string& n,int a) : name(n), age(a) {}
  Dude(int a)                      : name("Anonyme"), age(a) {}
  Dude(const std::initializer_list<std::string>& lst) : name(), age(lst.size()) {for(auto& elem : lst) name += elem;}
};

int main(int argc, char* argv[]) {
  Dude pierre    = {std::string("Pierre"), 42};
  Dude paul      = {42};
  Dude paul2     =  42 ;
  Dude paul3       {42};
  Dude jacques   = {std::string("Jacques"), "Yves", "Cousteau"};
  // Dude badguy = std::string("Terminator");    // Compiling error
  Dude badguy    = {std::string("Terminator")};
  // Dude nobody = {0.5};                        // Compiling error

  Dude paul4 {.name = "Paul", .age=32};
  // Dude paul5 {.age = 32, .name = "Paul"};
  return 0;
}
#include <iostream>
#include <string>
#include <array>
#include <vector>
#include <map>

struct Dude {
  std::string name;
  int age;
};

int main(int argc, char* argv[]) {
  std::vector<int>   v {1, 2, 3, 5, 7, 11, 13, 17, 19}; // Initializer list, any size is allowed.
  std::array<int, 3> a {1, 2, 3};                       // The size has to fit the array parameter.

  std::vector<Dude>          d      {{"Paul", 42}, {"Pierre", 31}, {"Jacques", 12}};
  std::map<std::string, int> age_of {{"Paul", 42}, {"Pierre", 31}, {"Jacques", 12}};
  return 0;
}

Constant expressions (constexpr)

This enable to compute at compiling time when it is feasible.

#include <iostream>
#include <array>

constexpr int syr_next(int i) {if(i%2 == 0) return i/2; return 3 * i + 1;                }
constexpr int syr_size(int i) {if(i   == 1) return   1; return 1 + syr_size(syr_next(i));}

#define SEED 27 

int main(int argc, char* argv[]) {
  //
  // Computation at compiling time
  //
  
  int i;
  std::array<int, syr_size(SEED)> values; 
  
  values[0] = SEED;
  for(i = 0; values[i] != 1 /* sounds dangerous */; ++i) values[i+1] = syr_next(values[i]);

  std::cout << "The last index is " << i << std::endl
	    << "The " << syr_size(SEED)  
  	    << " values of syracuse serie from " << SEED  << " are " << std::endl;
  for(auto val : values) std::cout << val << ' ';
  std::cout << std::endl;
  
  //
  // Computation at execution time
  //

  int seed;
  std::cout << "Enter a seed: " << std::flush;
  std::cin >> seed;
  std::cout << "The serie length is "
	    << syr_size(seed)             
	    << std::endl;
  return 0;
}
Hervé Frezza-Buet,