Some common features of C++11 (stability and compatibility)

Some common features of C++11 (stability and compatibility)

1, Original literal

The literal quantity defining the original string is added in C++11. The definition method is: R "xxx (original string) xxx", in which the strings on both sides of () can be omitted. The original literal R can directly represent the actual meaning of the string without additional operations such as specificity or connection.

For example, in the process of programming, the string used often contains some special characters. These characters often need special processing. This problem can be easily solved by using the original literal quantity, such as print path:

#include<iostream>
#include<string>
using namespace std;
int main()
{
    string str = "D:\hello\world\test.text";
    cout << str << endl;
    string str1 = "D:\\hello\\world\\test.text";
    cout << str1 << endl;
    string str2 = R"(D:\hello\world\test.text)";
    cout << str2 << endl;
	system("pause");
    return 0;
}


The output results are:

D:helloworld    est.text
D:\hello\world\test.text
D:\hello\world\test.text

At D: \ Hello \ world \ test The escape of \ h and \ w in text failed, and the corresponding characters will be output as is
At D: \ Hello \ world \ test The spacing character of the path in text is \ but this character is also an escape character, so you need to escape it with an escape character to get an ordinary character without special meaning
The original literal is used in R"(D:\hello\world\test.text)". The content in R () is the original string describing the path without any processing

Through the test, we can see that using the original literal R can directly obtain the string with its original meaning. Let's take another example of outputting HTML Tags:

#include<iostream>
#include<string>
using namespace std;
int main()
{
    string str = "<html>\
        <head>\
        <title>\
        One Piece\
        </title>\
        </head>\
        <body>\
        <p>\
        I'm the man who wants to be the pirate king!!!\
        </p>\
        </body>\
        </html>";
    cout << str << endl;
    return 0;
}

Before C++11, if a string is written to different lines, it needs to add connectors. This method is not only cumbersome, but also destroys the original meaning of the expression. If the original literal quantity is used, it becomes much simpler, intuitive and readable.

#include<iostream>
#include<string>
using namespace std;
int main()
{
    string str = R"(<html>
        <head>
        <title>
        One Piece
        </title>
        </head>
        <body>
        <p>
        I'm the man who wants to be the pirate king!!!
        </p>
        </body>
        </html>)";
    cout << str << endl;

    return 0;
}

Finally, emphasize one detail: in R "xxx(raw string)xxx", the original string must be enclosed by brackets (). Other strings can be added before and after the brackets. The added string will be ignored, and the added string must appear on both sides of the brackets at the same time.

#include<iostream>
#include<string>
using namespace std;
int main()
{
    string str1 = R"(D:\hello\world\test.text)";
    cout << str1 << endl;
    string str2 = R"luffy(D:\hello\world\test.text)luffy";
    cout << str2 << endl;
#if 0
    string str3 = R"luffy(D:\hello\world\test.text)robin";	// Syntax error, compilation failed
    cout << str3 << endl;
#endif

    return 0;
}

The output of the test code is:

D:\hello\world\test.text
D:\hello\world\test.text

From the output information, we can get the following conclusion: using the original literal R "xxx (raw string) xxx", () the strings on both sides will be ignored by the compiler during parsing, so it is generally not specified. If a string is specified before and after (), the string before and after () must be the same, otherwise a syntax error will occur.

2, final and override

The final keyword is added in C + + to restrict that a class cannot be inherited or a virtual function cannot be rewritten. The function of the final keyword is similar to that of Java. If you use final to modify functions, you can only modify virtual functions, and you should put the final keyword behind the class or function.

1. final modifier function

If final is used to modify the function, only the virtual function can be modified, which can prevent the subclass from overriding the function of the parent class:

#include<iostream>
#include<string>
using namespace std;

class Base
{
public:
    virtual void test()
    {
        cout << "Base class...";
    }
};

class Child : public Base
{
public:
    void test() final
    {
        cout << "Child class...";
    }
};

class GrandChild : public Child
{
public:
    // Syntax error, cannot be overridden because the base class test function is modified by final
    void test()
    {
        cout << "GrandChild class...";
    }
};

There are three classes in the above code:

Base class: Base
Subclass: Child
GrandChild

test() is a virtual function in the base class. This method is rewritten in the subclass, but you don't want to continue rewriting this method in the grandchildren. Therefore, mark the test() method as final in the subclass, and the grandchildren will only use this method. You can't rewrite this method anymore.

1.2 final modification class

A class decorated with the final keyword is not allowed to be inherited, that is, this class cannot have derived classes.

class Base
{
public:
    virtual void test()
    {
        cout << "Base class...";
    }
};

class Child final: public Base
{
public:
    void test()
    {
        cout << "Child class...";
    }
};

// Error, syntax error, because the child class inherits from Base, which is final inheritance, no more derived classes are allowed
class GrandChild : public Child
{
public:
};

1.3 override keyword

override keyword ensures that the rewritten function declared in the derived class has the same signature as the virtual function of the base class. At the same time, it also clearly indicates that the virtual function of the base class will be rewritten, which can ensure the correctness of the rewritten virtual function and improve the readability of the code. Like final, this keyword should be written behind the method. The method of use is as follows:

class Base
{
public:
    virtual void test()
    {
        cout << "Base class...";
    }
};

class Child : public Base
{
public:
    void test() override
    {
        cout << "Child class...";
    }
};

class GrandChild : public Child
{
public:
    void test() override
    {
        cout << "Child class...";
    }
};

Lines 13 and 22 in the above code show the test() method that specifies the parent class to be rewritten. After using the override keyword, assuming that the function name or function parameter or return value is written incorrectly due to misoperation in the rewriting process, the compiler will prompt syntax errors, improve the correctness of the program and reduce the probability of error.

Three, template optimization

1. Right angle bracket of template

In generic programming, template instantiation has a very cumbersome place, that is, two consecutive right angle brackets (> >) will be parsed into a right shift operator by the compiler, rather than the end of the template parameter table. Let's take a look at a piece of code about container traversal. The created class template Base provides the operation function traversal():

// test.cpp
#include <iostream>
#include <vector>
using namespace std;

template <typename T>
class Base
{
public:
    void traversal(T& t)
    {
        auto it = t.begin();
        for (; it != t.end(); ++it)
        {
            cout << *it << " ";
        }
        cout << endl;
    }
};


int main()
{
    vector<int> v{ 1,2,3,4,5,6,7,8,9 };
    Base<vector<int>> b;
    b.traversal(v);

    return 0;
}

If you compile the above code using the C++98/03 Standard, you will get the following error prompt:

test.cpp:25:20: error: '>>' should be '> >' within a nested template argument list
     Base<vector<int>> b;

According to the error prompt, a space needs to be added between the two right angle brackets describing the template, which is very troublesome to write. C++11 improves the parsing rules of the compiler and parses multiple right angle brackets (>) into template parameter terminators as much as possible, which is convenient for us to write code related to the template.

There is no problem in compiling the above code in the compiler supporting C++11. If g + + is used for direct compilation, the parameter - std = c++11 is required:

$ g++ test.cpp -std=c++11 -o app
2. Default template parameters
#include <iostream>
using namespace std;

template <typename T=int, T t=520>
class Test
{
public:
    void print()
    {
        cout << "current value: " << t << endl;
    }
};

int main()
{
    Test<> t;
    t.print();

    Test<int, 1024> t1;
    t1.print();

    return 0;
}

However, the default template parameters of the function are not supported. Support for the default parameters of the function template is added in C++11:

#include <iostream>
using namespace std;

template <typename T=int>	// C++98/03 does not support this writing method, which is supported in C++11
void func(T t)
{
    cout << "current value: " << t << endl;
}

int main()
{
    func(100);
    return 0;
}

From the above example, we can draw the following conclusion: when all template parameters have default parameters, the call of function template is like an ordinary function. However, for class templates, even if all parameters have default parameters, they must be instantiated with < > after the template name.

In addition, the default template parameters of the function template are different from other default parameters in the use rules. There is no restriction that they must be written at the end of the parameter table. In this way, when the default template parameters are combined with the automatic derivation of template parameters, the writing is very flexible. We can specify that some template parameters in the function template use default parameters and the other part use automatic derivation, such as the following example:

#include <iostream>
#include <string>
using namespace std;

template <typename R = int, typename N>
R func(N arg)
{
    return arg;
}

int main()
{
    auto ret1 = func(520);
    cout << "return value-1: " << ret1 << endl;

    auto ret2 = func<double>(52.134);
    cout << "return value-2: " << ret2 << endl;

    auto ret3 = func<int>(52.134);
    cout << "return value-3: " << ret3 << endl;

    auto ret4 = func<char, int>(100);
    cout << "return value-4: " << ret4 << endl;

    return 0;
}

The output of the test code is:

return value-1: 520
return value-2: 52.134
return value-3: 52
return value-4: d

Based on the output of the log, the template function called in the example code is analyzed.

auto ret = func(520);

The return value type of the function uses the default template parameter. The parameter type of the function is automatically derived and is of type int.

auto ret1 = func<double>(52.134);

The return value of the function is specified as double type, and the function parameters are derived from the arguments, which are double type

cauto ret3 = func<int>(52.134);

The return value of the function is specified as type int, and the function parameters are derived from the arguments, which are of type double

auto ret4 = func<char, int>(100);

The parameter of the function is specified as int type, and the return value of the function is specified as char type. No derivation is required
When the default template parameter and template parameter auto derivation are used at the same time (priority from high to low):

If the parameter type can be derived, the derived type is used
If the function template cannot derive the parameter type, the compiler uses the default template parameters
If the template parameter type cannot be derived and the default template parameter is not set, the compiler will report an error.

Consider the following example:

#include <iostream>
#include <string>
using namespace std;

// Function template definition
template <typename T, typename U = char>
void func(T arg1 = 100, U arg2 = 100)
{
    cout << "arg1: " << arg1 << ", arg2: " << arg2 << endl;
}

int main()
{
    // Template function call
    func('a');
    func(97, 'a');
    // func();    // Compilation error
    return 0;
}

The result of program output is:

arg1: a, arg2: d
arg1: 97, arg2: a

Analyze the template function func():

func('a '): parameter T is automatically derived as char type, and the default template parameter used by U is char type
func(97, ‘a’);: Parameter T is automatically derived as int type, and U uses the derived type as char
func();: Parameter T does not specify a default template type and cannot be derived automatically. The compiler will directly report an error
The automatic derivation of template parameter types is inferred based on the arguments specified during the template function call. If there are no arguments, it cannot be deduced
Automatic derivation of template parameter types does not refer to the default parameters specified in the function template.

4, Conversion between numeric type and string

Special type conversion functions are provided in C++11, which can be easily used to convert between numeric types and string types.

1. Convert numeric value to string

Use to_ The string () method can easily convert various numeric types to string types. This is an overloaded function. The function declaration is located in the header file. The function prototype is as follows:

// Header file < string >
string to_string (int val);
string to_string (long val);
string to_string (long long val);
string to_string (unsigned val);
string to_string (unsigned long val);
string to_string (unsigned long long val);
string to_string (float val);
string to_string (double val);
string to_string (long double val);

The use of functions is very simple. The example code is as follows:

#include <iostream>
#include <string>
using namespace std;

int main()
{
    string pi = "pi is " + to_string(3.1415926);
    string love = "love is " + to_string(5.20 + 13.14);
    cout << pi << endl;
    cout << love << endl;
    return 0;
}

2. Convert string to numeric value

Since the numeric types in C + + include integer and floating-point types, different functions are provided for different types. By calling these functions, the string type can be converted to the corresponding numeric type.

The prototype of the function is as follows:

// Defined in header file < string >
int       stoi( const std::string& str, std::size_t* pos = 0, int base = 10 );
long      stol( const std::string& str, std::size_t* pos = 0, int base = 10 );
long long stoll( const std::string& str, std::size_t* pos = 0, int base = 10 );

unsigned long      stoul( const std::string& str, std::size_t* pos = 0, int base = 10 );
unsigned long long stoull( const std::string& str, std::size_t* pos = 0, int base = 10 );

float       stof( const std::string& str, std::size_t* pos = 0 );
double      stod( const std::string& str, std::size_t* pos = 0 );
long double stold( const std::string& str, std::size_t* pos = 0 );

str: string to convert
pos: outgoing parameter, which character the record starts from and cannot be parsed. For example: 123abc, the outgoing position is 3
Base: if the base is 0, the value will be automatically detected. If the prefix is 0, it will be octal. If the prefix is 0X or 0X, it will be hexadecimal. Otherwise, it will be decimal.

Although these functions have multiple parameters, they all have default values except the first parameter. Generally, the requirements can be met by using the default values. An example is also provided for the use of functions. The example code is as follows:

#include <iostream>
#include <string>
using namespace std;
int main()
{
    string str1 = "45";
    string str2 = "3.14159";
    string str3 = "9527 with words";
    string str4 = "words and 2";

    int myint1 = std::stoi(str1);
    float myint2 = std::stof(str2);
    int myint3 = std::stoi(str3);
    // Error: 'std::invalid_argument'
    // int myint4 = std::stoi(str4);

    cout << "std::stoi(\"" << str1 << "\") is " << myint1 << endl;
    cout << "std::stof(\"" << str2 << "\") is " << myint2 << endl;
    cout << "std::stoi(\"" << str3 << "\") is " << myint3 << endl;
    // cout << "std::stoi(\"" << str4 << "\") is " << myint4 << endl;
    
    return 0;
}

The results of the sample code input are as follows:

std::stoi("45") is 45
std::stof("3.14159") is 3.14159
std::stoi("9527 with words") is 9527

From the above test program, it can be concluded that in the process of converting strings into values by these conversion functions provided by C++11:

If all characters in the string are numeric, the whole string will be converted to the corresponding numeric value and returned by the return value
If the character in the first half of the string is numeric and the character in the second half is not, the first half will be converted to the corresponding numeric value and returned by the return value
If the first character of a character is not a numeric value, the conversion fails

Author: Su Bingyu
Article link: https://subingwen.com/cpp/convert/
Copyright notice: unless otherwise stated, all articles on this blog adopt CC BY-NC-SA 4.0 license agreement. Reprint please indicate from love programming big C!

Keywords: C++ Back-end OOP C++11

Added by abhikerl on Wed, 26 Jan 2022 05:27:44 +0200