The static cast performs conversions between compatible types. It is similar to the C-style cast, but is more restrictive. For example, the C-style cast would allow an integer pointer to point to a char.
char c =10; // 1 byteint*p = (int*)&c; // 4 bytes
Since this results in a 4-byte pointer pointing to 1 byte of allocated memory, writing to this pointer will either cause a run-time error or will overwrite some adjacent memory.
*p =5; // run-time error: stack corruption
In contrast to the C-style cast, the static cast will allow the compiler to check that the pointer and pointee data types are compatible, which allows the programmer to catch this incorrect pointer assignment during compilation.
// example in a templated classtypedef pcl::PointCloud<PointInT> PointCloudIn;typedeftypename PointCloudIn::Ptr PointCloudInPtr; // need typename due to Ptrtypedeftypename PointCloudIn::ConstPtr PointCloudInConstPtr; // need typename// example in a templated functiontemplate <typenamePointT> // needed for templated argument type dependent on PointTvoidfunc(typename pcl::PointCloud<PointT>::Ptr& cloud){ // if creating a new instance dependent on PointTtypename pcl::KdTreeFLANN<PointT>::Ptr tree;}// example error message without typename keyword//// error: need ‘typename’ before ‘pcl::KdTreeFLANN<PointT>::Ptr’ //// because ‘pcl::KdTreeFLANN<PointT>’ is a dependent scope// one more exampletemplate <typenameFeatureT>classMyPerception{public:usingPointT= pcl::PointXYZRGBNormal;usingNormalT= pcl::PointXYZRGBNormal;usingPointCloudPtr= pcl::PointCloud<PointT>::Ptr; // not neededusingKeypointPtr= pcl::PointCloud<PointT>::Ptr; // not neededusingFeaturePtr=typename pcl::PointCloud<FeatureT>::Ptr; // needed};
template keyword is needed to tell the compiler explicitly that .cast is a template
// In PCL gicp.hpp// I would like to save the transformation matrix in each iteration for visualizationrigid_transformation_estimation_(output, source_indices,*target_, target_indices, transformation_);transformations_.push_back(transformation_.template cast<double>());
classMyClass {public:MyClass (): point(0.1) {};typedef boost::shared_ptr<MyClass> Ptr;typedef boost::shared_ptr<const MyClass> ConstPtr;voidmem_func (MyClass::Ptr ptr) const;float point;};// not allowed to change the member variables of MyClass::Ptr (which is the raw pointer)voidfunc(const MyClass::Ptr ptr){ptr->point =1.0; // OKptr.reset(); // error: not able to reset to null or to other instance}// not allowed to change the member variables of MyClassvoidfunc(MyClass::ConstPtr ptr){ptr->point =1.0; // error: not able to change the valueptr.reset(); // OK}void MyClass::mem_func(MyClass::Ptr ptr) const { point =1.0; // error: not allowed to change member variablesthis->point =1.0; // error: not allowed to change member variablesptr->point =1.0; // OK (external pointer)}
mutable
Recall that the const reference or pointer is constrained to
only read access for any visible data members,
permission to call only methods that are marked as const (because non-const member functions can write to data members, which breaks the fence).
The mutable keyword can allow you to modify a variable in a const method. It is like making exceptions for a few special variables.
pcl::PointCloud<PointT> cloud; // instancepcl::PointCloud<PointT>::Ptrcloud_ptr (new pcl::PointCloud<PointT>); //smart pointerauto cloud_raw_ptr =new pcl::PointCloud<PointT>; // raw pointer *cloud_ptr // instance*cloud_raw_ptr // instance typeid(*cloud_ptr) == typeid(*cloud_raw_ptr)&cloud // raw pointer&(*cloud_ptr) // will turn the smart pointer into a raw pointerstd::cout <<typeid(*cloud_ptr).name() << std::endl;
iterator
There are five types of iterator available in STL.
Bidirectional Iterator available for std::list,std::set and std::map
Random Access Iterator available for std::vector and std::deque
No iterator available for std::stack, std::queue and std::priority_queue
std::vector<int> vec{1,2,3,4,5};std::vector<int>::iterator it;for (it =vec.begin(); it <vec.end(); ++it) std::cout <<*it <<"\n";// begin() points to the first element in the container// end() points to the element after the last element (empty)
function pointer
// in keypoint.htypedef boost::function<int (int,double, std::vector<int>&, std::vector<float>&)> SearchMethod;// class Search in search.hvirtualintradiusSearch (int index,double radius, std::vector<int>&k_indices, std::vector<float>&k_sqr_distances,unsignedint max_nn =0) const;// in pcl::Keypoint<PointInT, PointOutT>::initCompute ()int (KdTree::*radiusSearch)(int index,double radius, std::vector<int>&k_indices, std::vector<float>&k_distances,unsignedint max_nn) const=&KdTree::radiusSearch;search_method_ = boost::bind (radiusSearch, boost::ref (tree_), _1, _2, _3, _4,0);// boost::ref is useful for passing references to function templates (algorithms)// that would usually take copies (values) of their arguments.// the simplest function pointervoid (*fun_ptr)(int) =&fun; // fun_ptr is a pointer to function fun()
Explicit (full) template specialization: Allows customizing the template code for a given set of template arguments.
Partial template specialization: Allows customizing class [and variable (since C++14)] templates for a given category of template arguments.
// 1) Template declaration (no specialization)template <classKey,classValue>structFoo {};// 2) Partial specializationtemplate <classKey>structFoo<Key,int> {};// 3) Full/explicit specializationtemplate <>structFoo<std::string,int> {};// Then, when instantiating the templates, // the compiler will choose the most specialized definition available:Foo<std::string, std::string> f1; // Should match #1Foo<int,int> f2; // Should match #2Foo<std::string,int> f3; // Should match #3// #1 and #3 work for template functions as well.// Examples in OpenCV (traits.hpp)template<typename_Tp> classDataType{public:typedef _Tp value_type; ...};template<> classDataType<bool> { ... };template<> classDataType<uchar> { ... };template<> classDataType<char> { ... };...
Enum: helps to assign constants to a set of names to make program easier to read, maintain and understand
Enums are not actual variables; they're just a semi-type-safe form of #define. They're a way of storing a number in a reader-friendly format. The compiler will transform all uses of an enumerator into the actual numerical value.
Union: helps to store data of different types as a single unit
// Enum example in OpenCV (traits.hpp)// template<> class DataType<bool>enum { generic_type =0, depth = CV_8U, channels =1, fmt = (int)'u', type =CV_MAKETYPE(depth, channels) };// Enum example in point cloud registrationenumclassRegistrationMethod { PCL, MTPCL, FAST };structRegistrationResult { RegistrationMethod method;double time_elapsed;double fitness; Eigen::Matrix4d transformation;};// Union example in PCL (point_types.hpp)// PointXYZLABunion{struct {float L;float a;float b; };floatdata_lab[4];};
For returning two values we can use a std::pair (usually typedef'd). In C++11 and newer, there's std::tuple for more than two return results. With introduction of structured binding in C++17, returning std::tuple should probably become accepted standard.
In practice, if multiple result values are needed, the best practice is to create a struct to store these values and return this struct or change it as the pass-by-reference argument.
// in C++ 11#include<tuple>std::tuple<int,int> divide(int dividend,int divisor) {return std::make_tuple(dividend / divisor, dividend % divisor);}#include<iostream>intmain() {int quotient, remainder; std::tie(quotient, remainder) =divide(14,3);}// Example in Open3D// open3d/pipelines/registration/ColoredICP.cppEigen::Matrix6d JTJ;Eigen::Vector6d JTr;double r2;std::tie(JTJ, JTr, r2) = utility::ComputeJTJandJTr<Eigen::Matrix6d, Eigen::Vector6d>( compute_jacobian_and_residual, (int)corres.size());// open3d/utility/Eigen.cpptemplate <typenameMatType,typenameVecType>std::tuple<MatType,VecType,double> ComputeJTJandJTr( std::function<void(int,VecType&,double&,double&)> f,int iteration_num,bool verbose /*=true*/) { ... }// Example in TEASER++namespace teaser {structRegistrationSolution {bool valid =true;double scale; Eigen::Vector3d translation; Eigen::Matrix3d rotation; EIGEN_MAKE_ALIGNED_OPERATOR_NEW};}
It is a keyword to tell compiler that this variable may be modified from outside the program which compiler is not aware of. Therefore, please do not optimize this variable.
It is a qualifier that can also be applied to methods/functions.
volatile and const are two faces of the same coin.
// Example without volatileint some_int =100;while (some_int ==100) { // compilier may optimize this to while(true) // code not related to some_int}// Example with volatilevolatileint some_int =100; // tells compiler not to optimize this variablewhile (some_int ==100) { // code not related to some_int}
This refers to a set of instructions to synchronize memory access (read/writes occur in the order you expect). For example a 'full fence' means all read/writes before the fence are comitted before those after the fence. It is a hardware level concept, and in higher level languages we are used to dealing with mutexes and semaphores.
Note that the keyword volatiledoes not guarantee a memory barrier to enforce cache-consistency. Although It is guaranteed that volatile reads/writes will happen in the exact order specified in the source code, but volatile reads/writes can still be reordered with respect to non-volatile ones. (This is referred to as compiler reordering optimizations.)
Thread #1 Core #1:
while (f ==0); // Memory fence required here print x;
By default, std::pair and std::tuple are not hashable, and therefore they cannot serve as keys in std::unordered_map. For hashing and combining multiple values, one way is to use boost::hash_combine function. Alternatively, we can write our own hash functions.
/** * @brief Templated hash function for Eigen::Matrix class. * * Example usage: * std::unordered_map<Eigen::Vector3i, double, hash_matrix<Eigen::Vector3i>> * * @note This implementation is oblivious to the storage order of Eigen matrix * (column- or row-major). It will give you the same hash value for two different * matrices if they are the transpose of each other in different storage order. * * The code is from `hash_combine` function of the Boost library. */template<typenameT>structhash_matrix: std::unary_function<T, std::size_t> { std::size_toperator()(Tconst& matrix) const {size_t seed =0;for (size_t i =0; i <matrix.size(); ++i) {auto elem =*(matrix.data() + i); seed ^= std::hash<typename T::Scalar>()(elem) +0x9e3779b9+ (seed <<6) + (seed >>2); }return seed; }};/** * @brief An alternative hash function optimized to reduce collision rate for * numerous vectors that all contain elements from a small continuous distribution. */std::size_toperator()(std::vector<uint32_t> const& vec) const { std::size_t seed =vec.size();for(auto x : vec) { x = ((x >>16) ^ x) *0x45d9f3b; x = ((x >>16) ^ x) *0x45d9f3b; x = (x >>16) ^ x; seed ^= x +0x9e3779b9+ (seed <<6) + (seed >>2); }return seed;}
pcl::Registration<PointT,PointT>::Ptr base_ptr;if (method =="pcl") base_ptr = std::make_shared<pcl::GeneralizedICP<PointT,PointT>>();elseif (method =="pclmt") base_ptr = std::make_shared<pcl::MultithreadedGeneralizedICP<PointT,PointT>>();elseif (method =="fast") base_ptr = std::make_shared<fast_gicp::FastGICP<PointT,PointT>>();elsethrow std::runtime_error("unknown gicp method");// Then use base_ptr to call functions in derived class// Note: these functions must already exist in base class as virtual functionsbase_ptr->setInputSource(source);base_ptr->setInputTarget(target);