⬆️ ⬇️

Static analysis of C ++ code and new standard of C ++ 0x language

annotation

Introduction

1. auto

2. decltype

3. Link to temporary object (R-value reference)

4. Right angle brackets

5. Lambda functions (Lambdas)

6. Suffix return type syntax

7. static_assert

8. nullptr

9. New Standard Classes

10. New directions in the development of static code analyzers

Conclusion

Bibliographic list



annotation



The article describes new C ++ features described in the C ++ 0x standard and supported in Visual Studio 2010. By the example of PVS-Studio, the changes in the language will be reflected in the static code analysis tools.



Introduction



The new standard of C ++ is about to come into our lives. While it continues to be called C ++ 0x, although, apparently, its final name is C ++ 11. The new standard is already partially supported by modern C ++ compilers, for example, Intel C ++ and Visual C ++. Support is far from complete, which is quite natural. First, the standard has not yet been adopted, and secondly, even when it is adopted, it will take time for the compilers to work out its features.



Compiler developers are not the only ones who care about supporting the new standard. Innovations of the language should be promptly supported in static source code analysis tools. The new standard promises backward compatibility. Almost guaranteed old C ++ code will be correctly compiled by the new compiler without the need for any changes. However, this does not mean that a program that does not contain new language constructs can still be processed by a static analyzer that does not support the new C ++ 0x standard. We saw this in practice by trying to check with PVS-Studio a project created in the beta version of Visual Studio 2010. It’s all about the header files that already use new language constructs. For example, in the header file “stddef.h” you can see the use of the new decltype operator:

 namespace std {typedef decltype (__ nullptr) nullptr_t;  } 
Naturally, such constructions are syntactically incorrect for an analyzer that does not support C ++ 0x, and result either in stopping its operation or in an incorrect result. The need to support C ++ 0x in PVS-Studio by the time Visual Studio 2010 was released became obvious, at least to the extent that the new standard is supported by this compiler.

')

It can be stated that this problem was successfully solved by us and at the time of this writing, the site has a PVS-Studio 3.50 version that integrates into both Visual Studio 2005/2008 and Visual Studio 2010. Starting with PVS-Studio 3.50 in the tool support is provided for that part of C ++ 0x, which is implemented in Visual Studio 2010. Support is not ideal, such as when working with “right-angle brackets”, but we will continue to work on supporting the C ++ 0x standard in future versions.



In this article we will look at new features of the language, the support of which is implemented in the first edition of Visual Studio 2010. At the same time, we will look at these possibilities from different perspectives: what is a new feature, is there a connection with 64-bit errors, how was the new language supported in PVS-Studio and how its appearance affected the VivaCore library .



Note. VivaCore is a library for parsing, analyzing and transforming code. VivaCore is an open library and supports C and C ++ languages. Based on VivaCore, the PVS-Studio product is built and other software projects can be created on its basis .



The article brought to your attention can be called a report on research and support of the new standard in PVS-Studio. PVS-Studio tool diagnoses 64-bit and parallel OpenMP errors. But since at the moment a more relevant topic is the transition to 64-bit systems, preference will be given to examples that demonstrate detection of 64-bit errors with PVS-Studio.



1. auto



In C ++, as in C, the variable type must be specified explicitly. However, with the introduction of template types and templates of template metaprogramming in C ++, it is often the case that the type of an object is not so easy to write. Even in a fairly simple case, when iterating over the elements of an array, we need an iterator type declaration of the form:

 for (vector <int> :: iterator itr = myvec.begin (); 
      itr! = myvec.end (); 
      ++ itr) 
Such designs are very long and uncomfortable. To shorten the record, you can use typedef, but this creates new entities and adds little in terms of convenience.



C ++ 0x offers a way to mitigate this problem. In the new standard, the value of the auto keyword will be replaced. If earlier auto meant that a variable is being created on the stack, and implied implicitly if you did not specify something else (register, for example), now this is analogous to var in C # 3.0. The type of a variable declared as auto is determined by the compiler itself based on what this variable is initialized to.



It should be noted that the auto-variable will not be able to store values ​​of different types during one program run. C ++ still remains a statically typed language, and the indication of auto only tells the compiler to take care of the type definition itself: after initialization, changing the type of the variable will no longer be possible.



Now the iterator can be declared as follows:

 for (auto itr = myvec.begin (); itr! = myvec.end (); ++ itr) 
In addition to ease of writing and simplifying code, the auto keyword helps make the code more secure. Consider an example where auto will make code safe from the point of view of creating 64-bit applications:

 bool Find_Incorrect (const string * arrStr, size_t n)
 {
   for (size_t i = 0; i! = n; ++ i)
   {
     unsigned n = arrStr [i] .find ("ABC");
     if (n! = string :: npos)
       return true;
   }
   return false;
 }; 
This code contains a 64-bit error. The function behaves correctly when compiling a Win32 version and fails when building in Win64 mode. The error lies in using the unsigned type for the variable “n”, although the type string :: size_type must be used, which is returned by the find () function. In a 32-bit program, the type string :: size_type and unsigned are the same, and we get the correct results. In the 64-bit program, string :: size_type and unsigned stop coinciding. When the substring is not found, the find () function returns the value string :: npos equal to 0xFFFFFFFFFFFFFFFFui64. This value is truncated to the value 0xFFFFFFFFu and placed in a 32-bit variable. As a result, the condition 0xFFFFFFFFu == 0xFFFFFFFFFFFFFFFFui64 is false and it turns out that the Find_Incorrect function always returns true.



In this example, the error is not so terrible, it is so detected even by the compiler and even more so by the specialized analyzer Viva64 (included in PVS-Studio).



Compiler:

 warning C4267: 'initializing': 
 conversion from 'size_t' to 'unsigned int', possible loss of data 
Viva64:

 V103: Implicit type conversion from memsize to 32-bit type. 
More importantly, this error is possible and often occurs in the code due to inaccuracy when choosing a type to store the return value. It is even possible that the error occurred due to the reluctance to use the cumbersome construction of the type string :: size_type.



Now such errors are easy to avoid, without cluttering up the code. Using the type of "auto" we can write the following simple and reliable code:

 auto n = arrStr [i] .find ("ABC");
 if (n! = string :: npos)
   return true; 
The error disappeared by itself. The code has not become more difficult or less effective. Conclusion - the use of "auto" is rational in many cases.



The “auto” keyword will reduce the number of 64-bit errors or allow you to fix errors more gracefully. But the use of “auto” by itself does not at all relieve all 64-bit errors! This is just another language tool that makes the programmer’s life easier, but doesn’t do all the type-control work for him. Consider an example:

 void * AllocArray3D (int x, int y, int z,
                    size_t objectSize)
 {
   int size = x * y * z * objectSize;
   return malloc (size);
 } 
The function must calculate the size of the array and allocate the necessary amount of memory. It is logical to expect that in a 64-bit environment this function will be able to allocate memory for working with an array of 2000 * 2000 * 2000 type “double”. However, a call like “AllocArray3D (2000, 2000, 2000, sizeof (double));” will always return NULL, as if allocating such a memory is impossible. The real reason why the function returns NULL is an overflow error in the expression "int size = x * y * z * sizeof (double)". The variable “size” will take the value -424509440 and the further call to the malloc function does not make sense. By the way, the compiler will also warn about the danger of this expression:

 warning C4267: 'initializing': 
 conversion from 'size_t' to 'int', possible loss of data 
Hoping for “auto”, an inaccurate programmer can modify the code as follows:

 void * AllocArray3D (int x, int y, int z,
                    size_t objectSize)

 {
   auto size = x * y * z * objectSize;
   return (double *) malloc (size);
 } 
However, this does not eliminate at all, but only disguises the error. The compiler will no longer give a warning, but the AllocArray3D function will still return NULL.



The type of the size variable will automatically become size_t . But overflow occurs when evaluating the expression "x * y * z". This subexpression has the type “int” and only then the type will be expanded to “size_t” when multiplied by the variable “objectSize”.



Now this hidden error can be detected only using the Viva64 analyzer:

 V104: Implicit type conversion to memsize type in an 
 arithmetic expression. 
Conclusion - using "auto", you should still be careful.



Now let's take a quick look at how a new keyword was supported in the VivaCore library, on which the static analyzer Viva64 is built. So, the analyzer should be able to understand that the variable AA has the type “int” in order to warn (see V101 ) about the expansion of the variable AA to the type size_t:

 void Foo (int X, int Y)
 {
   auto AA = X * Y;
   size_t BB = AA;  // V101
 } 
First of all, a new table of tokens was compiled, which included new C ++ 0x keywords. This table is in the file Lex.cc and is named tableC0xx. In order not to modify the old code by processing the token “auto” (tkAUTO), the token “auto” in this table is named tkAUTOcpp0x.



In connection with the advent of the lexeme, the following functions were modified: isTypeToken, optIntegralTypeOrClassSpec. There is a new class LeafAUTOc0xx. In TypeInfoId, a new object class has appeared - AutoDecltypeType.



To encode the type “auto”, the letter 'x' is selected, which is reflected in the functions of the TypeInfo and Encoding classes. This, for example, features such as IsAutoCpp0x, MakePtree.



These fixes allow you to parse the code with the key "auto", which has a new meaning and save the type of objects in encoded form (the letter 'x'). However, this does not allow us to know which type the variable actually represents. That is, there is no functionality in VivaCore, which allows to find out that in the expression “auto AA = X * Y” the variable AA will have the type “int”.



This functionality is contained in the source code of Viva64 and is not included in the code of the VivaCore library. The principle lies in the additional work of calculating the type in the TranslateAssignInitializer method. After the right part of the expression is calculated, the connection (Bind) of the variable name with the type is changed.



2. decltype



In some cases it is useful to "copy" the type of some object. The “auto” keyword displays the type based on the expression used to initialize the variable. If there is no initialization, then the keyword "decltype" can be used to determine the type of expression at compile time. An example of code where the variable “value” will have the type returned by the function “Calc ()”:

 decltype (Calc ()) value;
 try {
   value = Calc (); 
 }
 catch (...) {
   throw;
 } 
You can use "decltype" for type declaration:

 void f (const vector <int> & a,
        vector <float> & b)
 {
   typedef decltype (a [0] * b [0]) Tmp;
   for (int i = 0; i <b.size (); ++ i)
   {
     Tmp * p = new Tmp (a [i] * b [i]);
     // ...
   }
 } 
Note that the type taken using decltype may differ from the type displayed by auto.

 const std :: vector <int> v (1);
 auto a = v [0]; decltype (v [0]) b = 1;
 // type a - int  
 // type b - const int & (return value
 // std :: vector <int> :: operator [] (size_type) const) 
Let's move on to an example where “decltype” can be useful from the point of view of 64 bits. The IsPresent function searches for an element in the sequence and returns “true” if it is found:

 bool IsPresent (char * array,
                size_t arraySize,
                char key)
 {
   for (unsigned i = 0; i <arraySize; i ++) 
     if (array [i] == key)
       return true;
   return false;
 } 
This function is unable to work in a 64-bit system with large arrays. If the variable arraySize has a value greater than UINT_MAX, then the condition "i <arraySize" will never be fulfilled and an eternal loop will occur.



If we use the "auto" keyword, this will not change anything:

 for (auto i = 0; i <arraySize; i ++) 
   if (array [i] == key)
     return true; 
The variable “i” will be of type “int”, since 0 is of type “int”. The correct fix is ​​to use decltype:

 for (decltype (arraySize) i = 0; i <arraySize; i ++) 
   if (array [i] == key)
     return true; 
Now the “i” counter has the “size_t” type as the “arraySize” variable.



Support for “decltype” in the VivaCore library is in many ways similar to the support for “auto”. Added new lexeme tkDECLTYPE. Added parsing function rDecltype in the Parser.cc file. In connection with the advent of the modification, the optIntegralTypeOrClassSpec function has undergone a modification. There was a new class LeafDECLTYPE.



To encode the type returned by the decltype operator, the letter 'X' is selected (the capital letter 'X', as opposed to the capital 'x', used for auto). In connection with this, the functionality of the TypeInfo and Encoding classes has changed. For example, functions WhatIs, IsDecltype, MakePtree.



The functionality for calculating types for the decltype operator is implemented in the Environment class and is part of the VivaCore library. The type is calculated at the moment of writing a new variable / type in the Environment (functions RecordTypedefName, RecordDeclarator, RecordConstantDeclarator). For calculating the type, the FixIfDecltype function is responsible.



3. Link to temporary object (R-value reference)



In the C ++ 98 standard, temporary objects can be passed to functions, but only as a constant reference (const &). Therefore, the function is not able to determine whether it is a temporary object or a normal one, which is also passed as const &.



In C ++ 0x, a new link type will be added - a link to a temporary object (R-value reference). His announcement is: "TYPE_NAME &&". It can be used as a non-constant, legally modifiable object. This innovation allows you to take into account temporary objects and implement the transfer semantics (Move semantics). For example, if std :: vector is created as a temporary object or returned from a function, you can create a new object by simply transferring all internal data from a new type of link. The constructor of the transport std :: vector through the resulting link to a temporary object simply copies the array pointer located in the link, which at the end is set to an empty state.



The transport constructor or transport operator can be declared as follows:

 template <class T> class vector {
   // ...
   vector (const vector &);  // copy constructor
   vector (vector &&);  // move constructor
   vector & operator = (const vector &);  // copy assignment
   vector & operator = (vector &&);  // move assignment
 }; 


From the point of view of analysis of 64-bit errors in the code, it does not matter for us, it is processed when declaring the type '&' or '&&'. Accordingly, the support of this innovation in VivaCore is quite simple. The changes affected only the optPtrOperator function of the Parser class. In it, we equally perceive both '&' and '&&'.



4. Right angle brackets



From the point of view of the C ++ 98 standard, the following construct contains a syntax error:

 list <vector <string >> lvs; 
To prevent it, you need to insert a space between the two right closing angle brackets:

 list <vector <string>> lvs; 
Standard C ++ 0x legitimized the use of double closing brackets when declaring template types, without the need to insert a space between them. As a result, it becomes possible to write a little more elegant code.



It is important to implement the support of this innovation in a static analyzer, since developers will be happy to stop writing extra spaces.



At the moment, the analysis of template type declarations with ">>" is not well implemented in VivaCore so far. In some cases, the analyzer is mistaken and, apparently, over time, the parts of the analyzer related to the template parsing will be substantially reworked. While in the code you can see the following ugly functions that are trying to determine by heuristic methods, we are dealing with the shift operator ">>" or with a part of the type declaration of the type "A <B <C >> D": IsTemplateAngleBrackets, isTemplateArgs. For those who are interested in how to approach this problem correctly, the following document will be useful: " Right Angle Brackets (N1757) ". Over time, we will improve the processing of right-angle brackets in VivaCore.



5. Lambda functions (Lambdas)



Lambda expressions in C ++ is a short form of writing anonymous functors (objects that can be used as a function). Consider a little story. In C, function pointers are used to create functors:

 / * callback function * /
 int compare_function (int A, int B) {
   return A <B;
 }
 
 / * declaration of the sort function * /
 void mysort (int * begin_items,
             int num_items,
             int (* cmpfunc) (int, int));
 
 int main (void) {
     int items [] = {4, 3, 1, 2};
     mysort (items,
            sizeof (items) / sizeof (int),
            compare_function);
     return 0;
 } 
Earlier in C ++, a functor was created using a class that has operator () overloaded:

 class compare_class {
   public:
   bool operator () (int A, int B) {
     return (A <B);
   }
 };
 	
 // declaration of the sort function
 template <class ComparisonFunctor> 
 void mysort (int * begin_items,
              int num_items,
              ComparisonFunctor c);
 
 int main () {
     int items [] = {4, 3, 1, 2};
     compare_class functor;
     mysort (items,
     sizeof (items) / sizeof (int),
     functor);
 } 
In C ++ 0x, we get the ability to declare a functor even more elegantly:

 auto compare_function = [] (char a, char b)
   {return a <b;  };

 char Str [] = "cwgaopzq";
 std :: sort (Str,
           Str + strlen (Str),
           compare_function);
 cout << Str << endl; 
We set the variable compare_function which is a functor and the type of which is determined by the compiler automatically. Then we can pass this variable to std :: sort. We can further shorten the code:

 char Str [] = "cwgaopzq";
 std :: sort (
   Str,
   Str + strlen (Str),
   [] (char a, char b) {return a <b;}
 );
 cout << Str << endl; 
Here "[] (char a, char b) {return a <b;}" is nothing more than a lambda function.



A lambda expression always starts with [] brackets, in which a capture list can be specified. Then comes an optional parameter list and an optional return type. The declaration ends directly with the function body. In general, the format for writing lambda functions is as follows:

 '[' [<gripping_list>] ']'
 ['(' <list_of_parameters> ')' ['mutable']]
 ['throw' '(' [<types of exceptions>] ')']
 ['->' <return type>]
 '{' [<body_function>] '}' 
The capture list indicates which objects from the outer scope are accessed by the lambda function:

Unfortunately, in this article it is not possible to pay more attention to the lambda functions. You can learn more about the lambda functions by visiting the resources listed in the bibliography at the end of the article. As a demonstration of the use of lambda functions, we consider the code of a program that outputs lines in increasing order of their length.



The program creates an array of strings and an array of indices. Then, the program sorts the row indices in such a way that the rows are arranged by increasing their length:

 int _tmain (int, _TCHAR * [])
 {
   vector <string> strings;
   strings.push_back ("lambdas");
   strings.push_back ("decltype");
   strings.push_back ("auto");
   strings.push_back ("static_assert");
   strings.push_back ("nullptr");

   vector <size_t> indices;
   size_t k = 0;
   generate_n (back_inserter (indices),
              strings.size (),
              [& k] () {return k ++;  });

   sort (indices.begin (),
        indices.end (),
        [&] (ptrdiff_t i1, ptrdiff_t i2)
        {return strings [i1] .length () <
                 strings [i2] .length ();  });

   for_each (indices.begin (),
            indices.end (),
            [& strings] (const size_t i)
            {cout << strings [i] << endl;  });

   return 0;
 } 
Note According to C ++ 0x, you can initialize the std :: vector arrays as follows:

 vector <size_t> indices = {0,1,2,3,4}; 
But while Visual Studio 2010 does not support such constructs.



The quality of the analysis of lambda functions in static analyzers must match the quality of the analysis of simple functions. In general, the analysis of lambda functions is similar to the analysis of simple functions, except that the lambda functions have a different scope.



PVS-Studio provides full error diagnostics in lambda functions. Consider a sample code containing a 64-bit error:

 int a = -1;
 unsigned b = 0;
 const char str [] = "Viva64";
 const char * p = str + 1;

 auto lambdaFoo = [&] () -> char
   {
     return p [a + b];
   };

 cout << lambdaFoo () << endl; 
This code works when compiling in Win32 mode and prints the letter 'V' on the screen. In Win64 mode, the program crashes due to an attempt to access the element number 0xFFFFFFFF. More details on this type of errors are described in the lessons on developing 64-bit C / C ++ applications - “ Lesson 13. Pattern 5. Address arithmetic ”.



When checking the above code, PVS-Studio displays a diagnostic message:

 error V108: Incorrect index type: p [not a memsize-type].  Use memsize type instead. 
Accordingly, the analyzer had to parse the lambda function and deal with the scope of the variables. Difficult, but necessary functionality.



With the support of lambda functions the most significant changes in VivaCore are connected. In the process of building a parse tree is now involved a new function rLambdas. The function is in the Parser class and is called from such functions as rInitializeExpr, rFunctionArguments, rCommaExpression. The rLambdas function parses the lambda functions and adds a new type of object to the tree - PtreeLambda. The PtreeLambda class is declared and implemented in the PtreeLambda.h and PtreeLambda files.



PtreeLambda is processed in the constructed tree by the TranslateLambda function. All logic on working with lambda functions is concentrated in VivaCore. Inside TranslateLambda, you will encounter a call to the GetReturnLambdaFunctionTypeForReturn function implemented in the PVS-Studio code. But this function serves for internal purposes PVS-Studio and the blank stub function GetReturnLambdaFunctionTypeForReturn in VivaCore will not affect the code analysis in any way.



6. Suffix return type syntax



There are cases when it is difficult to specify the type returned by the function. Consider an example of a template function that multiplies two values:

 template <class T, class U>
 ???  mul (T x, U y)
 {
   return x * y;
 } 
The type returned must be the type of the expression "x * y". But it is not clear what can be written instead of "???". The first idea might be to use “decltype”:

 template <class T, class U>
 decltype (x * y) mul (T x, U y) // Scope problem!
 {
   return x * y;
 } 
The variables “x” and “y” are declared after “decltype (x * y)” and such code, unfortunately, cannot be compiled.



The solution to this problem is to use the new syntax of returned values:

 template <class T, class U>
 [] mul (T x, U y) -> decltype (x * y)
 {
   return x * y;
 } 
Using brackets [] we generate a lambda function here and at the same time we say "the return type will be displayed or specified later." Unfortunately, although the example is correct, it is not compiled in Visual C ++ at the time of writing. However, we can use an alternative option (where Suffix return type syntax is also used):

 template <class T, class U>
 auto mul (T x, U y) -> decltype (x * y)
 {
   return x * y;
 } 
This code will be successfully compiled by Visual C ++ and we will get the desired result.



In the PVS-Studio 3.50 version, support for the new format of functions is only partially implemented. The constructions are completely understood by the VivaCore library, but PVS-Studio does not take into account the data types returned by these functions. You can get acquainted with the support of alternative function recording in the VivaCore library in the Parser :: rIntegralDeclaration function.



7. static_assert



In the C ++ 0x standard, the new keyword static_assert has appeared. Syntax:

 static_assert (expression, "error message"); 
If the expression is false, the specified error message is displayed and the compilation is terminated. Consider an example of using static_assert:

 template <unsigned n>
 struct MyStruct
 {
   static_assert (n> 5, "N must be more 5");
 };

 MyStruct <3> obj; 
When compiling this code, the Visual C ++ compiler will display the message:

 error C2338: N must be more 5
   xx.cpp (33): see reference to class template
   instantiation 'MyStruct <n>' being compiled 
   with
   [
     n = 3
   ] 
From the point of view of analyzing the code implemented by PVS-Studio, the static_assert construction is not of interest and therefore is ignored. A new lexeme tkSTATIC_ASSERT has been added to VivaCore. Encountering this lexeme, the lexer ignores it and all parameters related to the static_assert construction (implementation as Lex :: ReadToken).



8. nullptr



Prior to the C ++ 0x standard, C ++ did not have a keyword for the null pointer. The number 0 was used to denote it. However, using the NULL macro was considered a good style. When expanded, the NULL macro becomes 0, and there is no practical difference between them. Here is how the NULL macro is declared in Visual Studio:

 #define NULL 0 
In some cases, the absence of a special keyword to indicate a null pointer was inconvenient and even provoked the occurrence of errors. Consider an example:

 void Foo (int a)
 {cout << "foo (int a)" << endl;  }

 void Foo (char * a)
 {cout << "foo (char * a)" << endl;  }

 int _tmain (int, _TCHAR * [])
 {
   Foo (0);
   Foo (null);
   return 0;
 } 
Although the programmer may expect that different functions of Foo will be called in this code, this is not so. Instead of NULL, 0 having the type “int” will be substituted and when the program is started, the screen will print:

 Foo (int a)
 Foo (int a) 
To eliminate such situations, the nullptr keyword has been introduced in C ++ 0x. The nullptr constant is of type nullptr_t and is implicitly cast to any pointer type or to a pointer to class members. The nullptr constant is not implicitly cast to integer data types, except for the bool type.



Let's go back to our example and add a call to the “Foo” function with the argument nullptr:

 void Foo (int a)
 {cout << "foo (int a)" << endl;  }

 void Foo (char * a)
 {cout << "foo (char * a)" << endl;  }

 int _tmain (int, _TCHAR * [])
 {
   Foo (0);
   Foo (null);
   Foo (nullptr);
   return 0;
 } 
Now the screen will be printed:

 Foo (int a)
 Foo (int a)
 Foo (char * a) 
Although the nullptr keyword is of no interest from the point of view of searching for 64-bit errors, it is necessary to support it when parsing code. For this, a new lexeme tkNULLPTR has been added to VivaCore, as well as the class LeafNULLPTR. Creating objects of type LeafNULLPTR occurs in the function rPrimaryExpr. When calling the function LeafNULLPTR :: Typeof, the type “nullptr” is encoded as “Pv”, that is, “void *”. From the point of view of the existing tasks of analyzing the code in PVS-Studio this is enough.



9. New Standard Classes



The C ++ 0x standard introduces new standard classes related to namespace std. A number of these classes are already supported in Visaul Studio 2010. Examples include:



Since the listed entities are ordinary template classes, their appearance did not require any modification of PVS-Studio or the VivaCore library.



10. New directions in the development of static code analyzers



In the end, I would like to note one interesting point related to the use of C ++ 0x. New features of the language, on the one hand, correcting old defects, make the code safer and more efficient, but at the same time they also create new, as yet unknown traps, into which a programmer can fall. True, I can not tell you anything about them.



But it is possible to get into already known traps due to the fact that their diagnostics in new C ++ 0x constructions is implemented much worse or not implemented at all. Consider a small example demonstrating the use of an uninitialized variable:

 {
   int x;
   std :: vector <int> A (10);
   A [0] = x;  // Warning C4700
 }

 {
   int x;
   std :: vector <int> A (10);
   std :: for_each (A.begin (), A.end (),
     [x] (int & y)
     {y = x;  } // No Warning
   );
 } 
The programmer may hope to receive a warning from the compiler in both the first and second cases. But in the example with the lambda function, no warning will be issued (tested on Visual Studio 2010 RC, / W4). As before, there were not many other warnings to various dangerous situations. It takes time to implement detailed diagnostics.



We can expect a new round of development of static analyzers in terms of the search for potentially dangerous constructions that arise when using C ++ 0x constructs. We position our product PVS-Studio as a tool for testing modern programs. At the moment we understand this as 64-bit and parallel technologies. In the future, we plan to conduct research into the question of what potential problems can be expected when using C ++ 0x. If there are a lot of pitfalls, then maybe we will start creating a new tool for their diagnosis.



Conclusion



In our opinion, C ++ 0x introduces many positive points. The old code does not require immediate upgrading, although it may be modified over time during refactoring. The new code can already be written using new constructions. Thus, the beginning of using C ++ 0x looks rational now.



Bibliographic list



  1. Bjarne Stroustrup. C ++ 0x - the next ISO C ++ standard. http://www.viva64.com/go.php?url=304
  2. Visual C ++ Team Blog. Rvalue References: C ++ 0x Features in VC10, Part 2. http://www.viva64.com/go.php?url=305
  3. Sergey Olendarenko. C ++ 0x. Lambda expressions. http://www.viva64.com/go.php?url=306
  4. Maksim. C ++ 0x and solving initialization problems. http://www.viva64.com/go.php?url=307
  5. Wikipedia. C ++ 0x. http://www.viva64.com/go.php?url=301

Source: https://habr.com/ru/post/89494/



All Articles