Summary of new features in C++14

On the whole, C++14 is only a small version released. Based on the large version of C++11, some optimization and defect repair have been done. C++14 was officially approved and announced on August 18, 2014, and the release version was officially released on December 15 of the same year. This article will make a summary of the changes. I hope you can criticize and correct the areas that need to be improved and improved.

1 variable template

Variable template is a new feature in C++14, which can instantiate variables into different types. The definition method of variable template is as follows:

template < parameter list  > Variable declaration

In the above syntax, the variable declaration is the variable template name. There can be one or more formal parameter lists. The usage method is as follows:

template<class T>
constexpr T pi = T(3.1415926535897932385L);  // Variable template

In the actual coding, if you want to realize the above functions before C++14, you need to get them through function templates. As shown in the following code, you can get the same function:

template<class T>
6  std::make_unique method T Pi()
{
  return T(3.1415926535897932385L);
}

2 variable template

When auto is taken as the formal parameter type in programming, it can be considered as generic lambda.

In C++14, generic lambda is an upgraded version of ordinary lambda. The specific use methods are as follows:

2.1 with two formal parameters

int main () {
 auto glambda = [](auto a, auto&& b) { return a < b; };
 bool b = glambda(3, 3.14); 
 std::cout<<"b="<<b<<std::endl;
  return 0;
}

2.2 with 1 formal parameter

int main () {
// Generic lambda, operator() is a template with a formal parameter
auto vglambda = [](auto printer) {
    return [=](auto&&... ts) // Generic lambda, ts is a formal parameter package
    { 
        printer(std::forward<decltype(ts)>(ts)...);
        return [=] { printer(ts...); }; // Zero element lambda (formal parameters are not accepted)
    };
};
auto p = vglambda([](auto v1, auto v2, auto v3) { std::cout << v1 << v2 << v3; });
auto q = p(1, 'a', 3.14); // Output 1a3.14
//q();    
  return 0;
}

2.3 uncapped generics

int main () {
    auto func1 = [](auto i) { return i + 4; };
    std::cout << "func1: " << func1(6) << '\n';
    return 0;
}

Code output value: 10

The above code can also be modified to the type with default values in lambda expression parameters. This writing method is also supported from C++14. After the code is slightly modified, the program is as follows:

int main () {
    auto func1 = [](int i = 6) { return i + 4; };
    std::cout << "func1: " << func1() << '\n';
    return 0;
}

Run this code and the result will be consistent with the above.

3 constexpr relax restrictions

After using the constexpr descriptor, the value of the specified variable or function can be used in a constant expression.

In C++11, constexpr can only use recursion. After C++14, it is optimized and promoted, and local variables and loops can be used. You don't have to write all the statements in one return statement.

constexpr int add(int a,int b)
{
    int sum = 0;
    if(a<=0) return -1;
    if(b<=0) return -1;
    sum = a+b;
    return sum;
}
int main () {
    std::cout<<add(4,6)<<std::endl;
    return 0;
}

As shown in the above code, constexpr is not supported before C++14.

4 binary literal

The literal quantity beginning with 0b or 0b is added in C++14. The usage method is as follows:

int main () {
   int x=32;
   int y = 0b100000;
   std::cout<<(x==y)<<std::endl;
    return 0;
}

The code output result is: 1

5 function return value derivation

In C++11, the post type is used to deduce the function return value. From C++14, it can be omitted. The return value uses auto. The compiler directly deduces the return statement value type in the function body. Note that:

  • If there are multiple return statements, the return value type of each statement should be consistent.
  • If there is no return statement or the argument of the return statement is a void expression, the declared return type must be decltype(auto) or auto. At this time, the derived return type is void.
  • Once you see a return statement in the function, the return type derived from the statement can be used for the rest of the function.
  • If the return statement uses a brace init list, derivation is not allowed.

See the following code for the use method:

//Post is not required in C++14
auto add(int x, int y)
{
  return x + y;
}
//The return value types of multiple return statements are consistent
auto f(bool val)
{
    if(val) return 123;   // Derived return type int
    else return 3.14f;    // Error: derived return type float
}
//There must be a return statement that can deduce the return type and use it in subsequent statements
auto sum(int i)
{
    if(i == 1)
        return i;              // The return type of sum is int
    else
        return sum(i - 1) + i; // OK, the return type of sum is known
}
//Derivation is not allowed for return value using {}
auto func () { return {1, 2, 3}; } // error

6 std::make_unipue method

C++14 provides std::make_unique method. The use method is as follows:

#include <iostream>
#include <memory>
 
struct Vec3
{
    int x, y, z;
    // The following constructors are no longer required since C++20
    Vec3(int x = 0, int y = 0, int z = 0) noexcept : x(x), y(y), z(z) { }
    friend std::ostream& operator<<(std::ostream& os, const Vec3& v)
    {
        return os << '{' << "x:" << v.x << " y:" << v.y << " z:" << v.z  << '}';
    }
};
 
int main()
{
    // Use the default constructor.
    std::unique_ptr<Vec3> v1 = std::make_unique<Vec3>();
    // Use constructors that match these parameters
    std::unique_ptr<Vec3> v2 = std::make_unique<Vec3>(0, 1, 2);
    // Create a unique that points to an array of 5 elements_ ptr 
    std::unique_ptr<Vec3[]> v3 = std::make_unique<Vec3[]>(5);
 
    std::cout << "make_unique<Vec3>():      " << *v1 << '\n'
              << "make_unique<Vec3>(0,1,2): " << *v2 << '\n'
              << "make_unique<Vec3[]>(5):   " << '\n';
    for (int i = 0; i < 5; i++) {
        std::cout << "     " << v3[i] << '\n';
    }
}

7 std::shared_timed_mutex,std::share_lock

The usage scenario of shared mutex is a scenario where the same data resource is read by multiple threads, but only one thread can modify it. As shown in the following code, the function of the assignment operator is that it can have multiple reads but only one write ability

class R
{
    mutable std::shared_timed_mutex mut;
    /* data */
public:
    R& operator=(const R& other)
    {
        // Exclusive ownership required to write * this
        std::unique_lock<std::shared_timed_mutex> lhs(mut, std::defer_lock);
        // Require shared ownership to read other
        std::shared_lock<std::shared_timed_mutex> rhs(other.mut, std::defer_lock);
        std::lock(lhs, rhs);
        /* Assignment data */
        return *this;
    }
};
int main() {
    R r;
}

8 std::integer_sequence class

The header file < utility > needs to be included when using. The prototype is defined as:

template< class T, T... Ints >
struct integer_sequence;

There is an open static member function size, which returns the number of Ints. The application method of modification is as follows:

template<typename T, T... ints>
void print_sequence(std::integer_sequence<T, ints...> int_seq)
{
    std::cout << "The sequence of size " << int_seq.size() << ": ";
    ((std::cout << ints << ","),...);
    std::cout << '\n';
}
int main()
{
    print_sequence(std::integer_sequence<unsigned, 9, 2, 5, 1, 9, 1, 6>{});
    return 0;
}

The code output result is: The sequence of size 7: 9,2,5,1,9,1,6,

9 STD:: exchange class

The function prototype is:

template< class T, class U = T >
T exchange( T& obj, U&& new_value );

Indicates the use of new_value replaces obj and returns obj. As shown in the following code:

void f() { std::cout << "f()"; }
 
int main()
{
   std::vector<int> v;
   std::exchange(v, {1,2,3,4});
   std::copy(begin(v),end(v), std::ostream_iterator<int>(std::cout,", "));
   std::cout << "\n\n";
   void (*fun)();
   std::exchange(fun,f);
   fun();
}

The running result of the above code is:

1, 2, 3, 4, 
f()

10 STD:: quoted class

The function prototype is:

template< class CharT >
/*unspecified*/ quoted(const CharT* s,
                       CharT delim=CharT('"'), CharT escape=CharT('\\'));

The implementation function is to allow string input or output with double quotation marks or other symbols. As shown in the following code:

int main()
{
    std::string in = "String with spaces, and embedded \"quotes\" too";
    std::cout<<std::quoted(in)<<std::endl;
    in = "String with spaces, and embedded $quotes$ too";
    const char delim {'$'};
    const char escape {'%'};
    std::cout << std::quoted(in, delim, escape);
}

The above code output is:

"String with spaces, and embedded \"quotes\" too"
$String with spaces, and embedded %$quotes%$ too$

The above is the main features added in C++14. Of course, there are some other improvements, which will not be described here. If you are interested, please leave a message for discussion.

reference resources:

https://en.cppreference.com/

Added by jaslife on Tue, 16 Nov 2021 10:30:39 +0200