Features Introduced in C++ 14

By | October 2, 2023

C++ 14 was planned as a minor release to complete the work that produced the C++11 standard, with the aim of becoming a cleaner, simpler, and faster language. Improvements in C++ 14 are “deliberately less” compared to C++ 11, says C++ creator Bjarne Stroustrup.

1. Binary Literals :

Binary literals provide a convenient way to represent a base-2 number. A binary integer literal (base two) begins with 0b or 0B and consists of a sequence of binary digits.
C++ 14 now supports binary literals. It is also possible to separate binary digits with the new ‘ digit separator to separate bytes or nybbles:

 
   // == 65
   auto a = 0b100'0001; 		

   // == 254
   auto b = 0b1111’1110 		

2. Digit Separator :

The single-quote character ‘ can now be used within a numeric literal for aesthetic readability. It does not affect the numeric value.

    auto millions = 1'000'000;
    auto pi = 3.14159'26535'897;

3. Generic Lambdas :

C++11 requires that lambda parameters to be declared with concrete types but now C++ 14 allows auto type-specifier in the parameter list, enabling polymorphic lambdas.

    // C++ 11
    auto lambda11 = [ ] (int x, int y) { return x + y; }; 

    // C++ 14   
    auto lambda14 = [ ] (auto x, auto y) {return x + y;};    

    // == 12
    int twelve = lambda14(3 , 9);			     

We can now express a lambda that will work with any suitable type:

  • This new version can be invoked with any type that has a .size() member function.
  • As it also implicitly deduces the return type, the return type will be whatever v.size() returns.
    auto v_size = [ ] (const auto& v) { return v.size(); };

4. Generalized Lambda Captures :

This allows creating lambda captures initialized with arbitrary expressions.

    int factory(int i) { return i * 10; }

    // returns 20
    auto f = [x = factory(2)] { return x; };    

    auto generator = [x = 0] () mutable {
        // this would not compile without 'mutable' 
        // as we are modifying x on each call
        return x++;
    };
    
    // == 0
    auto a = generator(); 

    // == 1
    auto b = generator(); 

5. Variable Templates :

Existing template syntax is simply added to a variable here. C++ 14 allows constexpr variables to be templated.
The idea is that despite having a single initializing expression, it’s sometimes useful to vary the type of a variable used in a generic function.

    template
    constexpr T pi  = T(3.14159265359);

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

6. Constexpr :

A constexpr-declared function in C++11 is a function which can be executed at compile time to produce a value to be used where a constant expression is required, such as when instantiating a template with an integer argument.

While C++11 constexpr functions could only contain a single expression, C++14 relaxes those restrictions by allowing conditional statements such as if and switch, and also allowing loops, including range-based for loops

C++14 also removes the C++11 rule that constexpr member functions are implicitly const.

C++14 now allows more things inside the body of constexpr functions, notably:

  • Local variable declarations (not static or thread_local, and no uninitialized variables).
  • Mutating objects whose lifetime began with the constant expression evaluation if, switch, for, while, do-while (not goto).

So in C++14, comparison function generalized to strings can use a normal loop directly:

    constexpr int my_strcmp( const char* str1, const char* str2 ) 
    {
        int i = 0;
        for( ; str1[i] && str2[i] && str1[i] == str2[i]; ++i )
            { }
        if( str1[i] == str2[i] ) return 0;
        if( str1[i] < str2[i] ) return -1;
        return 1;
   }

7. [ [deprecated] ] Attribute :

Allows marking an entity deprecated, which makes it still legal to use but puts users on notice that use is discouraged and may cause a warning message to be printed during compilation.

The attribute may be applied to the declaration of a class, a typedef-name, a variable, a non-static data member, a function, an enumeration, or a template specialization.

8. declType (auto) :

The decltype(auto) type-specifier also deduces a type like auto does. However, it deduces return types while keeping their references and cv-qualifiers, while auto will not.

    const int a = 0;

    // int
    auto a1 = a; 

    // const int              
    decltype(auto) a2 = a;     
	
    int b = 0;
    int& b1 = b;

    // int
    auto b2 = b1; 

    // int&
    decltype(auto) b3 = b1;   
	
    int&& c = 0;

    // int
    auto c1 = std::move(c);      

    // int&&       
    decltype(auto) c2 = std::move(c);    

	
    // Return type is `int`.
    auto f(const int& i) {
        return i;
    }

   // Return type is `const int&`.
   decltype(auto) g(const int& i) {
      return i;
   }

9. Return type deductions for functions :

C++ 14 allows return type deduction for all functions, thus extending C++ 11 that only allows it for lambda functions:

    auto DeduceFunctionReturnType();

Since C++14 is a strongly-typed language, a few restrictions shall be taken into account:

  • If a function’s implementation has multiple return statements, they must deduce the same type.
  • Return type deduction can be used in forward declarations.
  • It works for recursive function as well as long as atleast one return statement precedes the recursive call.
    auto f()             
    { 
        // ok
        return foo() * 42;       
    }     

    auto g()
    {
        while( something() ) 
        {
     	     if( expr ) {
                 return foo1() * 42;
       	     }
   	}
        return foo2(84);  
        //  Multiple returns  
        //  (types must be the same)                  
    }                                         



Reference –
http://www.open-std.org/jtc1/sc22/wg21/



Please write comments if you find anything incorrect. A gentle request to share this topic on your social media profile.