17. Standard Library
17.1 General
17.1.1 Do not use std::vector<bool>
The std::vector<bool> specialization does not conform to the requirements of a container and does not work as expected in all STL algorithms. In particular &v[0] does not return a contiguous array of elements as it does for other vector types. Additionally, the C++ language standard guarantees that different elements of an STL container can safely be modified concurrently, except for a container of std::vector<bool> type.
#include <cstdint> #include <vector> void foo () { std::vector <int32_t> vi; // @@+ Compliant +@@ std::vector <bool> vb; // @@- Non-Compliant -@@ }
References
- HIC++ v3.3 – 17.13
17.2 The C Standard Library
17.2.1 Wrap use of the C Standard Library
The C11 standard library, which is included in the C++ standard library, leaves the handling of concerns relating to security and concurrency up to the developer. Therefore, if the C standard library is to be used, it should be wrapped, with the wrappers ensuring that undefined behavior and data races will not occur.
#include <cstdio> #include <cerrno> bool foo () { std::puts ("hello world"); // @@- Non-Compliant -@@ return (0 == errno); // @@- Non-Compliant -@@ }
The wrapper code should be placed in separate source files, and this rule should be deviated for those files only.
References
- JSF AV C++ Rev C – 17
- MISRA C++:2008 – 19-3-1
17.3 General Utilities Library
17.3.1 Do not use std::move on objects declared with const or const & type
An object with const or const & type will never actually be moved as a result of calling std::move.
#include <cstdint> #include <utility> template <typename T> void f1 (T&&); // #1 template <typename T> void f1 (T const &); // #2 void f2 (int32_t const & i) { f1(i); // Calls #2 f1(std::move(i)); // @@- Non-Compliant: Calls #1 -@@ }
17.3.2 Use std::forward to forward universal references
The std::forward function takes the value category of universal reference parameters into account when passing arguments through to callees. When passing a non universal reference argument std::move should be used. Note: As auto is implemented with argument deduction rules, an object declared with auto && is also a universal reference for the purposes of this rule.
#include <utility> #include <cstdint> template <typename ...T> void f1 (T...t); template <typename T1, typename T2> void f2 (T1 && t1, T2 & t2) { f1( std::forward<T1>(t1) ); // @@+ Compliant +@@ f1( std::forward<T2>(t2) ); // @@- Non-Compliant -@@ f1( std::move(t1) ); // @@- Non-Compliant -@@ f1( std::move(t2) ); // @@+ Compliant +@@ } void f3() { int32_t i; f2(0, i); }
17.3.3 Do not subsequently use the argument to std::forward
Depending on the value category of arguments used in the call of the function, std::forward may or may not result in a move of the parameter. When the value category of the parameter is an lvalue, then modifications to the parameter will affect the argument of the caller. In the case of an rvalue, the value should be considered as being indeterminate after the call to std::forward (See Rule <hicpp ref=”decl.init.no-unset”/>).
#include <cstdint> #include <utility> template <typename T1, typename T2> void bar (T1 const & t1, T2 & t2); template <typename T1, typename T2> void foo (T1 && t1, T2 && t2) { bar (std::forward<T1> (t1), std::forward (t2)); ++t2; // @@- Non-Compliant -@@ } int main () { int32_t i = 0; foo (0, i); }
17.3.4 Do not create smart pointers of array type
Memory allocated with array new must be deallocated with array delete. A smart pointer that refers to an array object must have this information passed in when the object is created. A consequence of this is that it is not possible to construct such a smart pointer using std::make_shared. A std::array or std::vector can be used in place of the raw array type. The usage and performance will be very similar but will not have the additional complexity required when deallocating the array object.
#include <memory> #include <array> #include <vector> #include <cstdint> typedef std::vector<int32_t> int_seq; void foo () { // @@- Non-Compliant -@@ std::unique_ptr<int32_t[]> oa_1 (new int32_t[10]); // @@- Non-Compliant -@@ std::shared_ptr<int32_t> ob_1 (new int32_t[10] , std::default_delete<int32_t[]>()); // @@+ Compliant +@@ std::array<int32_t, 10> oa_2; // @@+ Compliant +@@ std::shared_ptr< int_seq > ob_2 (std::make_shared<int_seq>( 10 , int32_t() )); }
References
- Use API calls that construct objects in place – 17.4.2
17.3.5 Do not create an rvalue reference of std::array
The std::array class is a wrapper for a C style array. The cost of moving std::array is linear with each element of the array being moved. In most cases, passing the array by & or const & will provide the required semantics without this cost.
#include <array%gt; #include <cstdint> void f1(std::array<int32_t, 10> const &); // @@+ Compliant +@@ void f2(std::array<int32_t, 10> &&); // @@- Non-Compliant -@@
17.4 Containers Library
17.4.1 Use const container calls when result is immediately converted to a const iterator
The 2011 C++ language standard introduced named accessors for returning const iterators. Using these members removes an implicit conversion from iterator to const_iterator. Another benefit is that the declaration of the iterator object can then be changed to use auto without the danger of affecting program semantics.
#include <vector> #include <cstdint> void f(std::vector<int32_t> & v) { // @@- Non-Compliant -@@ for(std::vector<int32_t>::const_iterator iter = v.begin (), end = v.end () ; iter != end ; ++iter) { } // @@+ Compliant +@@ for(std::vector<int32_t>::const_iterator iter = v.cbegin (), end = v.cend () ; iter != end ; ++iter) { } // @@+ Compliant +@@ for(auto iter = v.cbegin (), end = v.cend () ; iter != end ; ++iter) { } }
17.4.2 Use API calls that construct objects in place
The 2011 C++ Language Standard allows for perfect forwarding. This allows for the arguments to a constructor to be passed through an API and therefore allowing for the final object to be constructed directly where it is intended to be used.
For Example:
# include <memory > # include <cstdint > void foo () { // Non-Compliant std :: shared_ptr < int32_t > p1 = std :: shared_ptr < int32_t >( new int32_t (0)); // Compliant std :: shared_ptr < int32_t > p2 = std :: make_shared < int32_t >(0); }
Analogous make_unique template is currently missing from the standard library, but one could easily be defined locally.
For Example:
# include <utility > namespace high_integrity { // make_unique not yet available in C++ ’11. This // version from : http :// herbsutter .com / gotw / _102 / template <typename T, typename ... Args > std :: unique_ptr <T> make_unique ( Args && ... args ) { return std :: unique_ptr <T >( new T( std :: forward <Args >( args )... ) ); } } using high_integrity :: make_unique ;
References
- Do not create smart pointers of array type 17.3.4
- Sutter Guru of the Week (GOTW) – 102
17.5 Algorithms Library
17.5.1 Do not ignore the result of std::remove, std::remove_if or std::unique
The mutating algorithms std::remove, std::remove_if and both overloads of std::unique operate by swapping or moving elements of the range they are operating over. On completion, they return an iterator to the last valid element. In the majority of cases the correct behavior is to use this result as the first operand in a call to std::erase.
#include <vector> #include <algorithm> #include <iostream> #include <cstdint> int main () { std::vector<int32_t> v1 = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4 }; std::unique(v1.begin(), v1.end ()); // @@- Non-Compliant -@@ // The possible contents of the vector are: // { 0, 1, 2, 3, 4, 2, 3, 3, 4, 4 }; std::vector<int32_t> v2 = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4 }; v2.erase (std::unique(v2.begin(), v2.end ()), v2.end ()); // @@+ Compliant +@@ }
References
- MISRA C++:2008 – 0-1-7