New Features of C++

introduce

      As you may have realized, the C++ language has been updated in the ISO standard. The encoding name for the new C++ language has been changed to C++0x, and many compilers have introduced some of its features. This guide will try to introduce you to the new features of the C++ language. Note that I'm only explaining some of the new features on the Visual C++ 2010 compiler, although these features have already been applied to other compilers. I dare not explain the absolute syntax rules on other compilers.

      This article assumes that you have a certain C++ language foundation, and understand type conversion, constant method, and know the template library (basic awareness).

New Features of C++

      The following list is some of the new features added to the C++ language that I'll be discussing. I've put more emphasis on lambdas and R-values ​​(these two new terms don't have a proper Chinese translation for the time being), since I haven't found anything digestible anywhere, so now, I won't use templates or the standard template library for simple examples, but I will definitely add and update this part of the content.

Keyword: auto keyword

Automatic data type evolution (at compile time) relies on assignment

Keyword: decltype

Infer data type from expression or auto variable

Keyword: nullptr

The null pointer has been advocated and evolved into a keyword!

Keyword: static_assert

For compile-time assertions. Useful for templates and validations where macro #ifdef cannot be used

Lambda expressions

logically defined function. Inherit from function pointers and class objects

Trailing return type

But the return type of the template cannot be an expression, which is useful

R-value quotes

Remove utilization of semantic resources before template objects are destroyed.

Other language features

The features of the C++ language must be included in VC8 and VC9 (VS2005, VS2008), and were not added to the C++ standard. These features have now been organized into C++0x.

This article will describe these features in detail (but not all).

let's start!


keyword 'auto'

The auto keyword now has more meanings. I assume you understand the original meaning of these keywords. Using this revised keyword you can declare a variable without specifying its data type.

For example:

auto nVariable = 16;

The above code declares the nVariable variable without specifying its type. Using the expression on the right, the compiler will determine the type of the variable. Code like the above will be translated by the compiler as follows:

int nVariable = 16;

You might assert that assignment to variables is now mandatory. Therefore, you cannot declare an automatic variable like this:

auto nVariable ; 

// Compilererror:

// errorC3531: 'nVariable': a symbol whose type contains

//              'auto' must have an initializer

Now, the compiler does not know the type of the variable nResult, use the auto keyword:

  • The type of a variable is determined at compile time, not run time.

Having said that, no matter how complex your task is, the compiler is still able to determine the data type. If the compiler cannot determine the type. It will generate an error. It's not like Visual Basic or a web scripting language.

a few examples

auto nVariable1 = 56 + 54; // deduction of int type

auto nVariable2 = 100 / 3;  // inference for int type

 

auto nVariable3 = 100 / 3.0; // deduction of double type. Since the compiler treats the whole expression as double

auto nVariable4 = labs(-127); // long, because the return value of the 'labs' function is long type

Let's get a little more complicated (continue with declaration below):

// nVariable3 is deduced to double.

auto nVariable5 =sqrt(nVariable3);

// Deduced double, because sqrt accepts 3 different data types, but we pass in double, override the return value

// double!

 

auto nVariable =sqrt(nVariable4);

// Error, because the sqrt call is ambiguous.

// This error is not related to the 'auto' keyword!

Inference of pointers:

auto pVariable6 =&nVariable1; // deduced as 'int*', since nVariable1 is int type.

auto pVariable =&pVariable6; // int**

auto* pVariable7 =&nVariable1; // int*

Quoted inferences:

auto & pVariable =nVariable1;  //int &

// Yes, the value of this variable will modify the variable it refers to!

Use the new operator:

auto iArray = new int[10]; // int*

Use const and volatile modifiers:

const auto PI = 3.14;  // double

volatile auto IsFinished = false;  // bool

const auto nStringLen = strlen("CodeProject.com");

disallowed scenarios

Arrays cannot be declared with automatic types:

 

auto aArray1[10];

auto aArray2[]={1,2,3,4,5};

// errorC3532: The type of the elements of the array type cannot be defined to contain 'auto'

Cannot be a parameter or return type of a function:

auto ReturnDeduction();

 

void ArgumentDeduction(auto x);

// C3533: 'auto': a variable of parameter type cannot be declared to contain 'auto'

If you need an auto return type or auto parameters, you can simply use templates!

You can't use auto in class or struct unless it's a static member;

struct AutoStruct

{

    auto Variable = 10;

    // errorC2864: 'AutoStruct::Variable' : Only static read-only member variables can be initialized in structures or classes

};

You cannot contain multiple data types in an automatic variable (types are inferred to be different types):

auto a=10, b=10.30, s="new";

// errorC3538: in a declarator-list 'auto'

// must be inferred to be the same data type

As above, if you use different functions to initialize variables, and one or more functions return different types of data types, the compiler will definitely generate the same error (C3538 below):

auto nVariable = sqrt(100.0), nVariableX =labs(100);

You can use the keyword auto at the global level.

This variable seems to be your old friend who knows everything, but is a curse if you abuse it. For example, a small number of programmers pretend to declare float in the following way, but it is actually double.

auto nVariable = 10.5;

Similarly, if a function returns an int. Next, you modify the type of its return value to be short or double, and the original automatic variable will become cautious. If you are unlucky and only have one variable declared with auto, the compiler will infer that variable as the new data type. And, if you're lucky, mixed with other type variables, the compiler will generate a C3538 error (above).

So when should we actually use the 'auto' keyword?

1. But when a data type may change depending on the compiler and/or target platform

For example:

int nLength = strlen("The auto keyword.");

May return a 4-byte integer in a 32-bit compilation environment, but may return 8-bit integers in a 64-bit environment. Pretty sure thing, you can use size_t instead of int or __int64. But what happens when the code compiles without defining size_t! Or does strlen return something else? In this case, you can simply use auto:

auto nLength = strlen("The auto keyword.");

2. When the representation of the data type is complex, or it confuses the code

std::vector<std::string>Strings; // string vector

 // Push severalstrings

 

// show them like this

for(std::vector<std::string>::iterator iter =

    Strings.begin(); iter !=Strings.end();  ++iter)

{  std::cout <<*iter << std::endl; }

You know, it's usually clumsy to type such a type std::vector<std::string>::iteratoriter . Although you can choose to use the keyword typedef in some places in the program to return the type and use the type name. But how much more for iterator types? And what if an iterator type is only used once? We shorten the encoding like this:

 

for(auto iter = Strings.begin(); iter != Strings.end(); ++iter)

{ std::cout << *iter <<std::endl; }

If you've used the STL for a while, you know about iterators and read-only iterators. As in the above example, you will also tend to use const_iterator instead of iterator. Therefore, you might want to use:

// Assume'using namespace std'

for(vector<string>::const_iteratoriter =

          Strings.begin(); iter !=Strings.end(); ++iter)

{ std::cout << *iter <<std::endl; }

Therefore, make the iterator read-only so that the elements of the vector cannot be modified. Remember that when you call begin on a const object, you can no longer assign it to a non-read-only iterator, and you must assign it to a const_iterator (const iterator is not the same as const_iterator, since I'm not writing the STL, please read and experiment with it yourself ) .

Recapitulating all the complexity and introducing the auto keyword, Standard C++ facilitates the use of the cbegin , cend , crbegin and crend ​​methods for STL containers . The C  prefix means read-only. They always return const_iterator iterators, regardless of whether the object (container) is const or not. The old method relies on object constness to return two types of iterators.

// Iterators are always read-only in the case of cbegin usage.

for(auto iter = Strings.cbegin(); iter!=Strings.cend(); ++iter) {...}

Another example on an iterator/read-only iterator:

map<std::vector<int>,string> mstr;

 

map<vector<int>,string>::const_iterator m_iter =mstr.cbegin();

The declaration of an iterator can be shortened to the following:

auto m_path = mstr.cbegin();

As with complex templates, you can use keywords to assign an untyped, error-prone function pointer, which may be assigned from other variables/functions. Since no examples are given, I assume you can see what I mean. 

3. Assign a Lambdas (anonymous program) to the variable

The following article is an explanation about lambdas.

4.  Specify a Trailing for the return type

Although anonymous programs are not involved, learning the (lambda) anonymous expression syntax is a must. I'll discuss it later in anonymous programs.

External citation


'decltype' keyword

C++ operators give the type of an expression. For example:

int nVariable1;

...

decltype(nVariable1)  nVariable2;

Declare nVariable2 as an int type. The compiler knows the type of nVariable1 and translates decltype(nVariable1) to type int. The decltype keyword is not the same as the typeid keyword. The typeid operator returns a type_info structure and requires RTTI to be enabled. Since it returns type information, not the type itself, you cannot use typeid like this:

typeid(nVariable1) nVariable2;

However, the expression is inferred to be a type, which is entirely at compile time. You cannot use decltype to get the type name (like 'int').

The combination of decltype and auto is a typical application. For example, you declare automatic variables as follows:

auto xVariable =SomeFunction();

Suppose xVariable (actually, the return type of SomeFunction) is of type X. Now, you can't call (or don't want to call) this function again. How do you declare another variable of the same type?

Which of the following would suit you better?

decltype(SomeFunc) yVar;

decltype(SomeFunc())yVar;

The first declares yVar as a function pointer, the second as type X. Using the function name is not very reliable, you can still access it, the compiler will not give an error or warning until you use the variable. Usually you have to pass the actual parameter values, and the actual types of the function/method parameters are overridden.

The recommended way is to infer directly from the type:

decltype(xVariable) yVar;

Also, since you've seen the discussion about auto, using template types is complicated and ugly, and you should use auto. For the same reason, you should use decltype to declare a correct type:

decltype(Strings.begin())string_iterator;

decltype(mstr.begin()->second.get_allocator()) under_alloc;

Unlike the previous example, however we use auto to infer the type from the expression on the right, we don't need to specify the type to infer. With decltype, you don't need to specify the variable, just declare it - because the type is known in advance. When you use the expression Strings.begin(), the function is not called, it just deduces the type from the expression. Similarly, when you put an expression in decltype, the expression is not evaluated. Only basic syntax checking is performed.

In the second example above, mstr is a std::map object, and we get back the iterator, which is the second member of the map element, and ultimately its allocator type. Therefore, the std:: allocator type is inferred for string (see above, mstr is declared).

There is almost no benefit, but it is a bit paradoxical. Examples are as follows:

decltype(1/0) Infinite; // Compiler does not generate an error for divisor by 0

//The program will not 'exit', and will save the type of exit(0).

//Specifying exit without making a function call makes the return type of 'MyExitFunction' different

decltype(exit(0)) MyExitFunction();

external reference

The type of decltype comes from - MSDN


Keyword 'nullptr'

Null pointers are finally classified as keywords! It is very similar to the NULL macro and integer 0. Although I'm only talking about native C++, know that keywords are used not only in native C++, but also in managed code. If you use C++ in mixed mode, you can explicitly use the keyword __nullptr to indicate a local null pointer, and use nullptr to indicate a managed null pointer. Even when programming in mixed mode, you don't use __nullptr very often.

void* pBuffer = nullptr;

...

if ( pBuffer == nullptr )

{ // Do something with null-pointer case }

 

 

// Withclasses

 

void SomeClass::SomeFunction()

{

   if ( this != nullptr)

   { ... }

}

Remember, nullptr is a keyword, not a type. Therefore, you cannot use operator sizeof or decltype for it. Having said that, the NULL macro and the keyword nullptr are two different entities. NULL is 0, which is actually an int type.  

For example:

void fx(int*){}

 

void fx(int){}

 

int main()

{

    fx(nullptr); // call fx(int*)

    fx(NULL);    // call fx(int)

 

}

Externalreferences

( Compiler requirements using the /clr option are wrong. MSDN has not been updated as of this writing.)


The 'static_assert' keyword

Using the static_assert keyword, you can specify some conditions at compile time. Here are the syntax rules:

static_assert(expression, message)

The expression is to be a compile-time constant expression. For non-template static declarations, the compiler recognizes the expression immediately. For template declarations, the compiler tests the assertion when the class is instantiated.

If the expression is true, it means the assertion you asked for has been satisfied, and the statement doesn't do anything. If the expression is false, the compiler will generate a C2338 error message. For example:

static_assert (10==9 , "Nine is not equal to ten");

Obviously, it is not true, so the compiler will generate the following error:

error C2338: Nine is notequal to ten

More meaningful assertions are generated when the program is not compiled with a 32-bit compiler:

static_assert(sizeof(void *) == 4,

       "Thiscode should only be compiled as 32-bit.");

Since the size of any pointer type is the same, when selecting the target platform for compilation.

In earlier compilers, we needed to use _STATIC_ASSERT , although it didn't do anything but declare the size of the array. So, if the condition is true, it will declare an array of size 1; if the condition is false, it will declare an array of size 0 - which will cause the compiler to generate an error. Errors are usually not friendly.

  • error C2466: cannot allocate an array of fixed size 0

External citation


Anonymous (Lambda) expressions

This is a compelling language feature added to C++. Very useful, interesting, and complex! I'll explain it with the most basic syntax and examples to make it clearer. So the following lines of code may not be characteristic of lambdas. But sure, lambdas are efficient and a feature of C++'s elegant simplicity!

Before I get into the discussion, let me first recap:

  • lambdas are more like a locally defined function. You can implement a lambdas anywhere in the program, which can be called as a normal expression or as a normal function (remember that in the previous VC++ compiler, it would report an error "Illegal definition of local function?").

The most basic lambda:

 []{};  // in some function/or code block instead of global.

Yes, the above declaration is perfectly legal (only in C++0x!).

[] is the introduction symbol for anonymous expressions, this symbol tells the compiler that the following expression is an anonymous. {} is the definition part of an anonymous expression, just like any function/method. The above anonymous expression doesn't pass any parameters, doesn't return any value, and of course doesn't do anything.

let's continue...

 []{ return 3.14159; }; // return a double

The above anonymous code can simply work: return the value of PI. But who will invoke this lambda? Where will the return value go? Let's discuss further:

double pi = []{ return 3.14159; }(); // Returnsdouble

Did you realize that? The return value is stored into a local variable pi. Normally, notice the example above where the anonymous function is called (note the final function call). Here is the minimal code to run the program:

int main()

{

   double pi;

   pi = []{return3.14159;}();

   std::cout <<pi;

}

The last parenthesis of the anonymous expression is a call to the anonymous function. Here, the anonymous expression does not carry any parameters, but the operator() is still required at the end to let the compiler know that this is a call. An anonymous expression for pi might be implemented like this:

pi = [](){return 3.14159;}(); // Notice the first parenthesis.

This expression is more like:

pi = [](void){return 3.14159;}(); // note the 'void'

Whether or not you put the argument in the first parentheses is a choice, though. But I'm more inclined to put in parameters. The C++ standards committee wanted to make anonymous expressions look a little simpler, which is (maybe) why they made anonymous parameters optional.

Let's go a step further and discuss with anonymous parameters:

bool is_even;

is_even = [](int n) { returnn%2==0;}(41);

The first parenthesis, (int n), specifies the argument of the anonymous expression. The second parenthesis, (41), passes in a value for the anonymous expression. The function of the main part of the anonymous expression is to determine whether the incoming number is divisible by 2. Now we can implement an anonymous expression to determine the maximum or minimum value, as follows:

int nMax = [](int n1, int n2) {

return (n1>n2) ? (n1) : (n2);

} (56, 11);

 

int nMin = [](int n1, int n2) {

return (n1<n2) ? (n1) : (n2);

} (984, 658);

Here, instead of declaring and assigning variables separately, I put them on the same line. Anonymous expressions now receive two parameters, return one of them, and this value is assigned to nMin or nMax. Similarly, anonymous expressions can pass in more parameters, and can also receive more parameter types.

You should have a few questions:

  • What is the return value? Are only ints valid?
  • What about anonymous expressions that return more than one status?
  • What if the anonymous expression needs to do something else, like display a value, perform another program step?
  • Can I store a reference into the defined anonymous expression and reference it elsewhere?
  • Can an anonymous expression call another anonymous expression or function?
  • An anonymous expression is defined as a local function, can it pass through the scope of the function?
  • What about an anonymous expression that can access a variable where it is defined or called? Can the value of this variable be modified?
  • Does it support default parameters?
  • How is it different from function pointers or function objects (functors)?

As I answer the above questions one by one, please let me use the following list to show you the syntax rules of anonymous expressions:

Q. What about the return value?

You can specify the return type after the operator ->. For example:

pi = []()->double{ return 3.14159; }();

Remember, as shown in the above table, if the anonymous function contains only one statement (that is, only one return statement), there is no need to specify the return type. Therefore, in the above example, specifying the return value to be double is optional. Whether or not you explicitly specify the return type for an automatically deducible return type is entirely up to you.

An example where the return type must be enforced:

int nAbs = [] (int n1) -> int

{

    if(n1<0)

       return-n1;

    else

       returnn1;

}(-109);

If you don't specify -> int , the compiler produces:

error C3499: an anonymous expression that has been specified with an empty return type cannot return a value

If the compiler doesn't notice a return statement: it infers that the anonymous function has a void return type. This return type can be any type.

 []()->int*{ }

[]()->std::vector<int>::const_iterator&{}

[](int x) ->decltype(x) { }; // Deducing type of 'x'

It cannot return an array. Nor can there be a return value of an automatic variable type:

 []()-> float[] {};  // error C2090: function returned an array

[]()-> auto {};    //error C3558: 'auto': the return value of an anonymous function cannot contain 'auto'

Of course, you can use an automatic variable to receive the return value of the anonymous function:

auto pi = []{return 3.14159;}();

 

auto nSum = [](int n1, int n2, int n3)

{ return n1+n2+n3; } (10,20,70);

 

auto xVal = [](float x)->float

{

    float t;

    t=x*x/2.0f;

return t;

} (44);

Finally, if you specify a return type for an anonymous function with parameters, you must use parentheses, and the following code will produce an error:

 []->double{return 3.14159;}();  // []()->double{...}

Q. What happens if an anonymous function has more than one return expression?

The above explanation is enough to show that anonymous functions can contain the code contained in any regular function. Anonymous functions can contain anything that functions\methods contain - local variables, static variables, calling other functions, memory allocation, and other anonymous functions! The following code is valid (albeit absurd!):

[]()

{

   static int stat=99;

   classTestClass

   {

      public:

        intmember;

   };

 

   TestClass test;

   test.member= labs(-100);

 

 

   int ptr =[](int n1) ->int*

   {

      int* p = new int;

      *p = n1;

      return p;

   }(test.member);

 

   delete ptr;

};

Q. An anonymous function needs to do something, such as display a value, what will happen to execute some other commands? Can I store a reference into an already defined anonymous function and reuse it in some places? An anonymous function is defined as a local function, can it be used across the scope of the function?

Let's define an anonymous function to check whether a number is even or not. Using the auto keyword, we can store anonymous functions in variables. So we can use this variable (that is, a call to an anonymous function)! I'll discuss types of anonymous functions next. A simple definition like this:

auto IsEven = [](int n) -> bool

{

     if(n%2 == 0)

        return true;

     else

        return false;

};  // no function call

As you might guess, the return value of the anonymous function is a bool, which takes one parameter. And importantly, we didn't call the anonymous function, we just defined it. If we use () and carry parameters, the variable type should be bool type, not anonymous function type! After the declaration above, now locally defined anonymous functions can be called.

IsEven(20);

 

if( ! IsEven(45) )

      std::cout <<"45 is not even";

The definition of IsEven, as given above, will be called twice in the same function. What happens if you want to call an anonymous function in another function? And here is an application, such as storing it in some local or class-level variables, and then passing it into another function (like a function pointer), and calling it from other functions. Another mechanism is to store and define a function in global scope. Since we didn't discuss the meaning of anonymous types, we'll discuss the first application later, so let's discuss the second application (global scope):

Example:

// The return value of the anonymous function is bool

// Anonymous function is stored to IsEven, using automatic type variable

auto IsEven = [](int n) -> bool

{

   if(n%2 == 0) return true;

   else return false;

}

 

void AnotherFunction()

{

   // call it!

   IsEven (10);

}

 

int main()

{

    AnotherFunction();

    IsEven(10);

}

Since auto is available at local and global scope, we can use it to store anonymous expressions. We need to know the type so we can store it in a class variable and use it later.

As stated earlier, anonymous functions can do almost everything regular functions do, so displaying a value is not out of reach for anonymous functions.

int main()

{

    using namespace std;

 

    auto DisplayIfEven= [](int n) -> void

    {

        if (n%2 == 0)

            std::cout <<"Number is even\n";

        else

            std::cout <<"Number is odd\n";

    }

   

    cout <<"Calling lambda...";

    DisplayIfEven(40);

}

One important corollary to note - a locally defined anonymous function does not get namespaced resolution from the parent scope it was defined in. So the scope of the std namespace is not available for DisplayIfEven.

Q. Can an anonymous function contain another anonymous or regular function?

It is already clear that you already know that providing an anonymous expression/function name is required at the time of the call, just as the function call is required within the function.

Q. Do anonymous functions support default parameters?

No.

Q. Can an anonymous function expression access variables from where it is defined or called? Can the variable content be modified? Is it any different from function pointers, or function objects (functors)?

Now I'll discuss my remaining issue: capture instructions.

Anonymous expressions can be the following cases:

  • status
  • stateless

State defines how variables are captured in a higher scope. I make it clear from the following categories:

  1. No variable is accessed in its parent scope. This is what we use all the time.
  2. Variables are accessed in read-only mode. You cannot modify the value of the parent variable.
  3. The variable is copied into the anonymous function (with the same name), and you can modify the copied variable, which is similar to the pass-by-value mechanism of the function.
  4. You have full access to the variable scope one level above, and you can modify the variable with the same name.

You will understand the following 4 advantages of inheriting from C++:

  1. private variable, you cannot access it.
  2. In a const (constant) method, you cannot modify the variable.
  3. Variables for functions/methods are passed by value.
  4. Variables are fully accessible within methods. In other words, variables are passed by reference.

Let's introduce capture! The description of the capture has been mentioned in the above figure and is given by [] . The following syntax is used to specify capture specifications:

  •  [] - nothing will be captured.
  • [=] – Capture by value.
  • [&] – Capture by reference.
  • [var] – captures var by value.
  • [&var] – Capture var by reference.

Example 1:

int a=10, b=20, c=30;

 

[a](void)  // capture 'a' by value only

{

    std::cout <<  "Value ofa="<<

    a <<std::endl;

       

    // cannot be modified

    a++;  // error C3491: 'a': a is captured by value

          // cannot be modified in a non-mutating anonymous expression

 

    // cannot access other variables

    std::cout <<b << c;

    // errorC3493: 'b' cannot be captured implicitly

// because no default capture mode was specified

}();

Example 2:

auto Average = [=]() -> float  // '=' means: capture all variables by value

{

    return ( a+ b + c ) / 3.0f;

 

   // cannot modify the value of any variable

};

 

float x = Average();

Example 3:

// Using '&' you specify that all variables are captured by reference

auto ResetAll =[&]()->void

{

    // Since you're capturing variables by reference, you can't modify them!

    a = b = c = 0;

};

 

ResetAll();

// the values ​​of a,b,c are set to 0;

Placing an = specifies to capture by value. Placing an & specifies capture by reference. Now let's explore a little bit more. In short, I'm not putting the anonymous expression into an automatic variable and calling it. Instead, I quote directly.

Example 4:

// capture only 'a' and 'b' by value

int nSum = [a,b] // Do you remember that () is optional for anonymous functions with no arguments?

{

    return a+b;

}();

 

std::cout << "Sum: "<< nSum;

As shown in Example 4, we can use the introduction symbol ([] operator) of an anonymous expression to capture multiple parameters. Let me take the second example, and the sum of all three parameters (a, b, c) is stored in nSum.

Example 5:

// Only 'nSum' is captured by reference, others are captured by value

[=, &nSum]

{

    nSum = a+b+c;

}();

In the above example, capturing all variables by value (= operator) specifies the default capture mode, and the expression &nSum overrides it. Note that the default capture mode for all captures must occur before other captures. Therefore = or & must appear before other specifications, and the following declaration will raise an error:

// & or = must appear first (if specified).

[&nI am,=]{}

[a,b,c,&]{} // Logically similar to the above, but wrong.

A few examples:

 [&, b]{}; //(1) capture by reference, except 'b' is captured by value

 [=, &b]{}; //(2) is the opposite of the above

[b,c, &nSum]; // (3) Capture 'b', 'c' by value

                  //'nSum' is the value capture.

[=](int a){} // (4) all capture by value, hides 'a' - because a is now a function parameter, bad practice, //compiler doesn't produce a warning            

[&, a,c,nSum]{}; // similar to (2)

[b, &a, &c,&nSum]{} // similar to (1)

[=, &]{} // invalid!

[&nSum, =]{} // invalid!

[a,b,c, &]{} // invalid!

As you can see, there are combinations of captures for the same set of variables. We can extend specified captures by adding:

  • [&,var] – captures by reference except var which is captured by value.
  • [=, &var] - captures by value except var which is captured by reference.
  • [var1, var2] – captures variables var1, var2 by value.
  • [&var1, &var2] – var1, var2 by reference.
  • [var1, &var2] – captures var1 by value and var2 by reference.

So far, we've seen that we can prevent some variables from being captured, from the const keyword by value, and from the non-const keyword by reference . So we've covered 1, 2 and 4 in the capture category above. It is not possible to capture a constreference (ie, [const&a] ). We'll now look at the last capture in the call-by-value pattern.

Description of 'mutable'

After the parameter specification brackets, we specify a mutable keyword. We put all variables captured by value into a call-by-value pattern. If we don't put a mutable keyword, all variables captured by value are read-only and you can't modify it inside the anonymous function. Placing a keyword mutable tells the compiler to force a copy of all variables captured by value. So you can then modify the variable captured by value. There is no way to selectively capture const or non-const variables by value. Or simply, you can assume that they are passed to the anonymous function as a parameter.

For example:

int x=0,y=0,z=0;

 

[=]()mutable->void // () is required when we specify a 'mutable' keyword

{

    x++;

// because all variables are captured in call-by-value mode

// The compiler will generate a warning because neither y, z are used

}();

// the value of x is still 0

After the anonymous function call, the value of x is still 0, because only a copy of x was modified, not a reference. It is also often interesting that the compiler produces a warning only for y and z and not for previously defined variables (a, b, c...). However, it won't complain that you are using a predefined variable. Smart compiler - I dare not say what will happen!

What is the difference between anonymous functions, function pointers, and function objects?

Pointers to functions do not preserve state, whereas anonymous functions do. With capture by reference, anonymous functions can preserve their state across invocations. Functions cannot. Function pointers are not type-safe, they are error-prone, we must strictly follow the calling convention and require complex syntax.

Function objects also hold state. But even for a small application, you have to write a class, and define some variables in the class, and overload the operator (). More importantly, you have to do this work outside the function block so that other functions that should call operator() for this class must know about it. This breaks the flow of the code.

What is the type of an anonymous expression?

Anonymous expressions are actually classes. You can store them in a function class object. This class, for anonymous expressions, is defined in the std::tr1 namespace. Let's look at an example:

#include<functional>

....

std::tr1::function<bool(int)>s IsEven = [](intn)->bool { returnn%2 == 0;};

...

IsEven(23);

The tr1 namespace is in Technical Report 1, which is used by the C++0x committee, and you can find more information on your own. <bool(int)> represents the template parameter for the function class, which means: the function returns a bool type, and an int type is passed in. Based on the mechanism that the anonymous expression is placed inside the function object, you must perform the type conversion correctly; otherwise, the compiler will generate errors or warnings due to type mismatches. However, as you can see, it is more convenient to use the auto keyword.

Here are some use cases, however, where you must use a function - when you need to pass an anonymous expression through a function. Here's an example:

using namespace std::tr1;

void TakeLambda(function<void(int)> lambda)

// cannot use 'auto' in function parameters

{

    // call it!

    lambda(32);

}

 

// Somewhere in the program...

TakeLambda(DisplayIfEven); // See above code 'DisplayIfEven'

DisplayIfEven anonymous expression (or function!) accepts int, returns void. The function class is used as a parameter in TakeLambda in the same way. Further, it calls the anonymous expression, which eventually calls the DisplayIfEven anonymous expression.

I've simplified TakeLamba, this expression should be (gradually shown):

// reference, should not copy the 'function' object

void TakeLambda(function< void(int) > & lambda);

 

// read-only reference, should not modify the function object

void TakeLambda(const function< void(int) > &lambda);

 

// fully qualified name

void TakeLambda(const std::tr1::function< void(int) > &lambda);

What is the purpose of introducing anonymous expressions in C++?

Anonymous expressions are useful for many STL functions -- ie functions that expect a function pointer or function object (using the operator() overload). In short, anonymous expressions are very useful for program routines that require callback functions. At first, I didn't cover the STL functions, but explained the usefulness of anonymous expressions in a simple and easy-to-understand form. The non-STL examples may seem redundant, but they help clarify the topic. 

For example, the arguments to the function below require a function to be passed in. It will call the function passed in. A function pointer, function object, or anonymous expression should be of the required type, returning a void type and accepting an int type parameter as the only parameter. 

void CallbackSomething(int nNumber, function<void(int)> callback_function)

{

    // call the specified 'function'

    callback_function(nNumber);

}

Here I call the CallbackSomething function in three different ways: 

// function

void IsEven(int n)

{

   std::cout <<((n%2 == 0) ? "Yes": "No");

}

 

// Class overloading of operator()

class Callback

{

public:

   void operator()(int n)

   {

      if(n<10)

         std::cout <<"Less than 10";

      else

         std::cout <<"More than 10";

   }

};                                     

 

int main()

{

   // pass a function pointing to

   CallbackSomething(10,IsEven);

 

   // pass a function object

   CallbackSomething(23,Callback());

 

   // another way..

   Callback obj;

   CallbackSomething(44,obj);

 

   // locally defined anonymous expression!

   CallbackSomething(59,[](int n)   { std::cout << "Half: " <<n/2;}     );

}

OK! Now I want the class to be able to show if a number is greater than N (instead of a constant 10). We can accomplish this task like this:

class Callback

{

   /*const*/int Predicate;

public:

   Callback(intnPredicate) : Predicate(nPredicate) {}

 

   void operator()(int n)

   {

      if( n < Predicate)

         std::cout <<"Less than " << Predicate;

      else

         std::cout <<"More than " << Predicate;

   }

};

In order to make this callable, we need an integer constant to construct it. The original function CallbackSomething doesn't need to be changed - it can still take an integer parameter to call the routine! This is how we do it:

// pass in a function object

CallbackSomething(23, Callback(24));

// 24 is the parameter of the CallBack constructor, not the parameter of CallbackSomething!

 

// another way..

Callback obj(99); // Set 99 to judge

CallbackSomething(44, obj);

In this way, we make the Callback class have the ability to save state. Remember, as long as the object exists, its state remains. So if you pass an obj object into multiple calls of CallbackSomething (or any other similar function), it will have the same assertion (state). As you know, this is absolutely impossible with function pointers - unless we introduce another parameter to the function. However, doing so destroys the entire structure of the program. If a particular function requires a callable function with a specified type, we can only call functions of that type. Function pointers do not hold state, so are not useful in such scenarios.

Is it possible to use anonymous expressions for such cases? As mentioned earlier, anonymous expressions are able to save state with specified captures. So, indeed, such inclusion of state can be accomplished using anonymous expressions. Here is a modified anonymous expression, stored in the auto variable:

int Predicate = 40;

 

// anonymous expressions are stored in 'stateful' variables

auto stateful = [Predicate](intn)

   {  if( n < Predicate)

           std::cout <<"Less than " << Predicate;

         else

           std::cout <<"More than " << Predicate;

   };

 

CallbackSomething(59, stateful ); // Morethan  40

   

Predicate=1000;

CallbackSomething(100, stateful); // Predicate NOT changed for lambda!

Anonymous expressions that maintain state are defined locally in a function, which is more concise than function objects and much clearer than function pointers. Now it has a state. So the first call will print "More than 40" and the same for the second call.

Note that the predicate value is called by value (non-volatile), so modifying the original value will not affect its state in the anonymous expression. In order to reflect the modification of the assertion in the anonymous expression, we only need to capture the variable by reference and it will be OK. When we change the anonymous expression as follows, the second call will print "Less than 1000".

auto stateful = [&Predicate](intn) // by reference

This is very similar to the mechanism of adding a method such as a class that can modify the value (state) of the assertion. Please check out the VC++ blog, linked below, which talks about Anonymous Expressions - Mapping of Classes.

use STL

STL's for_each function will call the specified function for each element in a range/collection, and because it uses templates, it can accept any type of data type as its parameter. We can use this feature as an example of anonymous expressions. Even simpler, I'll use basic arrays instead of vectors or lists, like this:

using namespace std;

    

int Array[10] = {1,2,3,4,5,6,7,8,9,10};

 

for_each(Array,&Array[10], IsEven);

 

for_each(Array, Array+10,[](int n){ std::cout <<n << std::endl;});

The first call calls the IsEven function, and the second call calls the anonymous expression, which is defined in the for_each function. It will call these two functions 10 times, because the extent of the array contains/specifies 10 elements. It's exactly the same for the for_each function and I don't need to repeat its second parameter (oh! but I did).

This is a very simple example where for_each and anonymous expressions can be exploited to display values ​​without writing a function or class. To be sure, anonymous expressions can be further extended to do additional work -- like printing whether a number is prime, or computing a sum (by using call-by-reference), or modifying the elements of a range.

Modify the formal parameters of anonymous expressions?

Yes, yes, you can do it, I've been talking about capturing and making modifications by reference, but not covering parameters that modify themselves. This need has never arisen until now. In order to do so, the parameters of the anonymous expression are only accepted by reference (or pointer):

Well, yes! You can do that. For long, I talked abouttaking captures by references and making modifications, but did not covermodifying the argument itself. The need did not arise till now. To do this,just take the lambda's parameter by reference (or pointer):

// 'n' is passed by reference (not the same as capture by reference!)

for_each(Array, Array+10,[](int& n){ n *= 4; });

The call to for_each above will multiply each element of Array by 4.

Just like I explained how to use anonymous expressions with the for_each function, you can use it with other <algorithm> functions like transform, generate, remove_if, etc. Anonymous expressions are not limited to using STL features. They can also be efficiently applied to any function object you need. You need to make sure to pass the correct number and type of parameters to him, check whether it needs parameter modification and the above needs. Since this document is not about the STL and templates, I won't go any further on this.

an anonymous expression cannot be used as a function pointer

Yes, disappointed and confused, but true! You cannot pass an anonymous expression as an argument into a function whose argument is a function pointer. Despite the sample code, let me first explain what exactly I want to express:

// Define a function pointer type with a parameter of type int

typedef void (*DISPLAY_ROUTINE)(int);

 

// Define a function that takes a function pointer as a parameter,

void CalculateSum(int a,int b, DISPLAY_ROUTINE pfDisplayRoutine)

{

   // call this function pointer

   pfDisplayRoutine(a+b);

}

CalculateSum takes a DISPLAY_ROUTINE function pointer type. The following code works because we give it a function pointer:

void Print(int x)

{

  std::cout << "Sum is: " <<x;

}

 

int main()

{

   CalculateSum(500,300, Print);

}

But the following code will have problems :

CalculateSum (10, 20, [](int n) {std::cout<<"sum is: "<<n;});

// C2664:'CalculateSum' : cannot convert parameter 3 from

//        '`anonymous-namespace'::<lambda1>'to 'DISPLAY_ROUTINE'

Why? Because anonymous expressions are object-oriented, they are actually classes. The compiler internally generates a model of the class for the anonymous expression. Internally generated classes will have operator() overloaded; and will have some data members (inferred from capture-specification and mutable-specification) - these may be read-only, reference, or regular member variables and class fills. Such a class cannot be downgraded to a regular function pointer.

How did the previous example work?

Well, because the smart class std::function! See (above) that CallbackSomething actually takes a function as an argument, not a function pointer.

      Such as for_each - this function does not pass std::function, but uses a template instead. He directly calls to pass parameters inside the parentheses. Seriously understand the simplified implementation:

template <class Iteartor, class Function>

void for_each(Iteartor first, Iterator, Function func)

// ignore the return value and other parameters

{

  // Assume the following call is a loop

  // 'func' can be a regular function, or can be an object of a class, the operator () operator is overloaded

 

  func(first);

}

Similarly, other STL functions, such as , , etc., can work in three situations: function pointers, function objects, and anonymous expressions.

      So, if you plan to use anonymous expressions in API functions like SetTimer, EnumFontFamilies, etc. - don't plan! Even casting the anonymous expression (by passing its address) won't work and the program will crash at runtime.

Guess you like

Origin blog.csdn.net/besidemyself/article/details/6707307