Read explanations of problems encountered in the project code (continuously updated)

Modify function parameters with const

If a parameter is used for output, no matter what data type it is, and whether it adopts "pointer passing" or "reference passing", it cannot be modified with const, otherwise the parameter will lose its output function. const can only modify input parameters:

  • If the input parameter adopts "pointer passing", then adding const modification can prevent the pointer from being accidentally changed and play a protective role.
  • If the input parameter adopts "value transfer", since the function will automatically generate a temporary variable to copy the parameter, the input parameter does not need to be protected, so do not add const modification. For example, do not write the function void Func1(int x) as void Func1(const int x). For the same reason, do not write the function void Func2(A a) as void Func2(const A a). Among them, A is a user-defined data type.
  • For parameters of non-internal data types, functions declared like void Func(A a) are destined to be relatively inefficient. Because the function body will generate a temporary object of type A for copying parameter a, and the construction, copying, and destruction of the temporary object will consume time. In order to improve efficiency, you can change the function declaration to void Func(A &a), because "pass by reference" only borrows the alias of the parameter and does not need to generate a temporary object. But the function void Func(A &a) has a disadvantage: "passing by reference" may change the parameter a, which we do not expect. It's easy to solve this problem, just add const modification, so the function eventually becomes void Func(const A &a).
  • By analogy, should void Func(int x) be rewritten as void Func(const int &x) to improve efficiency? It is completely unnecessary, because there is no construction or destruction process for internal data type parameters, and copying is also very fast. The efficiency of "value passing" and "reference passing" are almost the same.

Summarized as follows:

  • For input parameters of non-internal data types, the "value passing" method should be changed to "const reference passing" in order to improve efficiency. For example, change void Func(A a) to void Func(const A &a).
  • For input parameters of internal data types, do not change the "pass by value" method to "pass by const reference". Otherwise, the purpose of improving efficiency will not be achieved, and the understandability of the function will be reduced. For example void Func(int x) should not be changed to void Func(const int &x).

Use const to modify the return value of a function

  • If the function return value in the "pointer passing" method is modified with const, the content of the function return value (ie pointer) cannot be modified, and the return value can only be assigned to a pointer of the same type with const modification.
  • const member functions Any function that does not modify a data member should be declared of type const. If you accidentally modify a data member or call other non-const member functions when writing a const member function, the compiler will point out an error, which will undoubtedly improve the robustness of the program.
  • Some rules about Const functions:
    a. const objects can only access const member functions, while non-const objects can access any member functions, including const member functions.
    b. Members of const objects cannot be modified, but const objects pass The object maintained by the pointer can be modified.
    c. The const member function cannot modify the data of the object, regardless of whether the object has const properties. When it is compiled, it is checked based on whether the member data is modified. Add
    the mutablemodifier Data members can be modified by any means under any circumstances. Naturally, the const member function at this time can modify it.

express keyword

  • Specifies that a constructor or conversion function (since C++11) is explicit, i.e. it cannot be used for implicit conversions and copy-initialization.
  • The explicit specifier can be used with a constant expression. A function is explicit if and only if the constant expression evaluates to true. (Since C++20)
  • After the constructor is modified by explicit, it can no longer be called implicitly.

Constructor=delete or default

  • =deleteRepresents deletion of the default constructor
  • =defaultRepresents the default constructor

std::optional

std::optional<T>A variable of type is either a variable of type T or a state indicating "nothing".

https://blog.csdn.net/yuejisuo1948/article/details/118440275

std::make_optional

std::make_optional

 
定义于头文件 <optional>
		
template< class T >
constexpr std::optional<std::decay_t<T>> make_optional( T&& value );   (1) 	(C++17)
template< class T, class... Args >
constexpr std::optional<T> make_optional( Args&&... args );    (2) 	(C++17)
template< class T, class U, class... Args >
constexpr std::optional<T> make_optional( std::initializer_list<U> il, Args&&... args );   (3) 	(C++17)
  1. Creates an optional object from value. Equivalently calls std::optional<std::decay_t>(std::forward(value)) .
  2. Creates an in-place constructed optional object from args…. Equivalent to return std::optional(std::in_place, std::forward(args)…); .
  3. Creates an in-place constructed optional object from il and args…. Equivalent to return std::optional(std::in_place, il, std::forward(args)…); .

std::move()

std::moveIt cannot move anything, its only function is to coerce an lvalue into an rvalue reference , and then use the value through an rvalue reference for move semantics.

try_emplace()

Since std::mapthe key of an element is unique, we often encounter such a scenario. When inserting an element into a map, we first check whether the key specified by the map exists. If it does not exist, the insertion operation is performed. If it exists, it is taken out and used directly. Or when the key does not exist, the insertion operation is performed, and when the key exists, the update operation is performed.

The general approach is to directly use emplace operation to determine whether the specified key exists. If it does not exist, insert the element. When the element exists, emplace will still construct the element to be inserted. After judging that it does not need to be inserted, the element will be Destruction, the consequence of this is that redundant construction and destruction operations are generated.

In view of this, C++17 introduced the method std::try_emplaceof separating key and value in the parameter list. This method will detect whether the specified key exists. If it exists, do nothing. If it does not exist, insert the corresponding value.

CHECK macro and Debug macro (DCHECK macro)

  • DCHECK
  • DCHECK_GE
  • DCHECK_GT
  • DCHECK_LE
  • DCHECK_LT

Insert image description here
Reference:
https://www.cnblogs.com/yinheyi/p/12243832.html
https://www.cnblogs.com/DesignLife/p/16918862.html

mutable keyword

mutableIt is constset up to break through the limits. Modified mutablevariables will always be in a mutable state, even within a constfunction.

std::generate()

Assigns each element in the range [first, last) to the value generated by the given function object g.

void generate( ForwardIt first, ForwardIt last, Generator g );

https://zhuanlan.zhihu.com/p/597486495

constexpr usage

constconstexprThe main difference from variables is that initialization of const variables can be deferred until runtime . constexpr variables must be initialized at compile time. All constexprvariables are const.

https://learn.microsoft.com/zh-CN/cpp/cpp/constexpr-cpp?view=msvc-140

enum class

enum class Color{
    
    black,white,red}; //black、white、red作用域仅在大括号内生效

auto white = false;		//正确,这个white并不是Color中的white

Color c = white;	//错误,在作用域范围内没有white这个枚举量

Color c = Color::white;	//正确

auto c = Color::white;	//正确

https://blog.csdn.net/weixin_42817477/article/details/109029172

std::remove_if

c.erase(std::remove_if(t.begin(),t.end(),lambda判断函数),e): Can be used to delete all elements in t that meet the conditions

like:

  trimmers_.erase(
      std::remove_if(trimmers_.begin(), trimmers_.end(),
                     [](std::unique_ptr<PoseGraphTrimmer>& trimmer) {
    
    
                       return trimmer->IsFinished();
                     }),
      trimmers_.end());

remove_ifThe parameter is an iterator, and the first two parameters represent the starting position of the iteration and the stop position corresponding to this starting position. The last parameter: Pass in a callback function. If the callback function returns true, the parameter currently pointed to will be moved to the end. The return value is the first element of the moved area

https://zhuanlan.zhihu.com/p/141177036

Operator overloading

Reference link

Obfuscated solution eval()

In eigen, a=a.transpose();this writing method cannot be used! Direct operations on the original memory address will cause read and write conflicts! can be a=a.transpose().eval();used to solve this problem.
eval()Temporary variables can be used to solve the confusion problem that occurs when the memory addresses of the left and right variables are the same.

C++ std::vector::resize()

 C++ 11void resize (size_type n); void resize (size_type n, const value_type& val);
 C++ 98void resize (size_type n, value_type val = value_type());
 
  • If n is less than the size of the current container, reduce the contents to its first n elements and remove out-of-range elements (and destroy them).
  • If n is larger than the size of the current container, the contents are expanded by inserting the required number of elements at the end to reach the size of n. If val is specified, new elements are initialized to a copy of val, otherwise they are value-initialized.
  • If n is also greater than the current container capacity, the allocated storage space will be automatically reallocated.

std::is_arithmetic

Function: If T is an arithmetic type (that is, an integer type or a floating-point type ) or its cv-qualified version thereof, provide a member constant value equal to true. For any other type, the value is false.

https://blog.csdn.net/BIT_HXZ/article/details/124246675

reserve method of vector

The function of reserve is to change the capacity of the vector so that the vector can hold at least n elements.
If n is greater than the current capacity of the vector, reserve will expand the vector. The storage space of the vector will not be reallocated under other circumstances.

std::get

std::getIn addition to obtaining elements of std::tuple through index (C++11), elements can also be obtained through element type (C++14)

https://blog.csdn.net/luoshabugui/article/details/118681579

std::isfinite

Checks for having a finite value, i.e. it is normal, subnormal or zero, but not infinite or null.

std::optional -->value_or

value_orEither the value stored in the optional is returned, or the argument is returned without storage.

This lets you take the maybe-null optional and give a default behavior when you actually need a value. By doing it this way, the “default behavior” decision can be pushed back to the point where it is best made and immediately needed, instead of generating some default value deep in the guts of some engine.

https://riptutorial.com/cplusplus/example/14080/value-or

noalias() in Eigen

For matrix multiplication, Eigen will solve the confusion problem by default. If you are sure that there will be no confusion, you can use it noalias()to improve performance.

Perfect forwarding std::forward()

https://blog.csdn.net/coolwriter/article/details/80970718

When we pass an rvalue reference into a function, it is named in the actual parameter, so when we continue to pass it down or call other functions, according to the definition of the C++ standard, this parameter becomes an lvalue. Then it will never call the rvalue version of the following function, which may cause copies in some cases. In order to solve this problem, C++ 11 introduces perfect forwarding. Based on the inversion of rvalue judgment, the value passed out by calling forward. If it is originally an rvalue, then it will be an rvalue when it is converted, otherwise it will be an lvalue.
This processing perfectly forwards the left and right value attributes of the original parameters without causing unnecessary copies.

std::clamp

Function declaration:

template<typename T>
constexpr const T& clamp(const T& value, const T& min_value, const T& max_value);

Interval bounding function.
It can be simplified to the following definition:

template<class T>
T clamp(T x, T min, T max)
{
    
    
	if (x > max)
		return max;
	if (x < min)
		return min;
	return x;
}

std::lower_bound()和std::upper_bound()

  • The results returned by both functions are iterators

  • std::lower_bound(start, end, value)It is to find the position of the first value greater than or equal to value in the interval and return it. If it is not found, it returns end()the position.

  • Instead std::upper_bound(start,end,value), it finds the first position that is greater than value and returns it. If it cannot find it, it also returns end()the position.

https://blog.csdn.net/albertsh/article/details/106976688

std::remove_cv, std::remove_const, std::remove_volatile

https://en.cppreference.com/w/cpp/types/remove_cv

Insert image description here

std::distance()

Two iterators of the same type acting on the same container can effectively specify an interval range. On this basis, if you want to get the number of elements contained in the specified range, you can use distance()the function.

distance()The function is used to calculate the number of elements contained in the range [start, end) represented by two iterators.

#include <iostream>     // std::cout
#include <iterator>     // std::distance
#include <list>         // std::list
using namespace std;
 
int main() {
    
    
    //创建一个空 list 容器
    list<int> mylist;
    //向空 list 容器中添加元素 0~9
    for (int i = 0; i < 10; i++) {
    
    
        mylist.push_back(i);
    }
    //指定 2 个双向迭代器,用于执行某个区间
    list<int>::iterator first = mylist.begin();//指向元素 0
    list<int>::iterator last = mylist.end();//指向元素 9 之后的位置
    //获取 [first,last) 范围内包含元素的个数
    cout << "distance() = " << distance(first, last);
    return 0;
}

https://blog.csdn.net/u014072827/article/details/119456066

std::prev()

https://blog.csdn.net/qq_36268040/article/details/111036214

  • new_iterator = prev(iterator,n)

    When "n" is a positive number, return the iterator "new_iterator" to the left of the incoming iterator "iterator" and n units away from "iterator".

    When "n" is a negative number, return the iterator "new_iterator" that is n units away from "iterator" to the right of the incoming iterator "iterator".

  • new_iterator = prev(iterator)

    If n is not written, it will move 1 unit to the left of "iterator" by default.

std::back_inserter()

back-inserter()It is an iterator used to add elements to a container. It is designed to prevent the original elements in the container from being overwritten and automatically insert new elements at the end of the container.

https://en.cppreference.com/w/cpp/iterator/back_inserter.

boost::make_optional

boost::optionalUsing container semantics, objects that may produce invalid values ​​are wrapped, the concept of uninitialization is implemented, and a solution is provided for such invalid value situations. optional can be used directly in c++17. optional is located boost/optional.hppin.

boost::optionalThe core class of the library is optional, which is very much like a container that can only store one element. If the element is not initialized, the container is empty, otherwise the value in the container is a valid, initialized value.

https://zhuanlan.zhihu.com/p/337180080.

DCHECK related knowledge

DCHECK is a macro commonly used in C++ code for assertion checking during debugging. It is used in many open source projects such as Chromium, Google Test, etc.

  • Assertion: An assertion is a check inserted in a program to verify whether a certain condition is true. If the condition is false, the assertion will trigger an error, usually causing the program to terminate or enter debug mode. The purpose of assertions is to help developers catch errors and unexpected situations during development.

  • DCHECK macro: The DCHECK macro is an assertion check used for debugging purposes. It is similar to the assert macro, but unlike assert, the DCHECK macro always performs an assertion check in a debug build and triggers an error if the assertion fails. In release builds, the DCHECK macro is compiled as a no-op, thus avoiding runtime overhead.

  • DCHECK_EQ, DCHECK_NE, DCHECK_LT, etc.: In addition to DCHECK, there are a series of macros used to perform different types of assertion checks. These macros end with _EQ, _NE, _LT, etc., indicating relationships such as equal to, not equal to, less than, etc. For example, DCHECK_EQ(a, b) means checking whether a is equal to b.

  • Compiler flags: In order to enable or disable the checking of DCHECK, you can use compiler flags or macro definitions. In debug builds, DCHECK checking is enabled by default and disabled in release builds. Compile flags can be adjusted as needed to control the behavior of DCHECK.

  • Usage scenarios of DCHECK: The DCHECK macro is usually used for debugging and error checking during the development process. It helps developers catch potential bugs or non-expected situations during development and provides information about where the bug occurred.

CHECK related knowledge

  • CHECK Macro: The CHECK macro is used for run-time assertion checking. Unlike DCHECK, the CHECK macro performs assertion checks in both debug builds and realize builds, so an error will be triggered in any build. It can be used to check the legality of critical conditions at runtime and terminate program execution if the conditions are not met.

  • CHECK_EQ, CHECK_NE, CHECK_LT, etc.: In addition to CHECK, there are also a series of macros used to perform different types of assertion checks, similar to DCHECK. These macros end with _EQ, _NE, _LT, etc., indicating relationships such as equal to, not equal to, less than, etc. For example, CHECK_EQ(a, b) means checking whether a is equal to b.

  • Usage scenarios of CHECK: The CHECK macro is usually used to check the legality of key conditions at runtime and terminate program execution when the conditions are not met. It helps developers catch runtime errors and provides information about where the errors occurred. CHECK is usually used to perform necessary but not negligible checks to ensure the correctness and safety of the program.

  • Impact of CHECK: When a CHECK check fails, it triggers an error message and terminates program execution. In a debug build, error messages are usually printed and debug mode is entered so that developers can debug. In a release build, error messages are usually logged and then the program terminates.

constexpr and const

Both constexpr and const are used to declare constants in C++, but they have different use cases and semantics.

const is used to declare an unmodifiable value. It can be used for variables, function parameters, function return types, and class member variables.

const int MAX_VALUE = 100; // 声明一个常量
const int* ptr = &MAX_VALUE; // 指向常量的指针,指针本身可修改
void foo(const int value); // 声明一个接收常量参数的函数

In the above example, const ensures that the declared value cannot be modified . However, const constants are not necessarily evaluated at compile time, but at runtime.

In contrast, constexpr is used to declare a constant expression that can be evaluated at compile time. It can be used for variables, functions, constructors and class member functions (after C++11). Using constexpr can provide compile-time calculation capabilities, which is very useful in some scenarios where results need to be obtained at compile time.

constexpr int square(int x) {
    
    
    return x * x;
}

constexpr int MAX_VALUE = square(10); // 编译时计算常量

In the above example, the constexpr function square is calculated at compile time and can be used to initialize the constexpr variable MAX_VALUE. This way, MAX_VALUE will evaluate to 100 at compile time and can be used as a compile-time constant.

Therefore, constexpr is suitable for those cases where the constant expression is known and can be evaluated at compile time, while const is suitable for cases where an unmodifiable value needs to be declared at runtime.

std::get_if usage

std::get_if is a function in the C++ standard library header file that is used to extract a specific type of value from an object of type std::variant. std::variant is a polymorphic value type introduced in C++17 that can hold multiple different types and can determine at runtime which type is currently held.

The declaration of the std::get_if function looks like this:

template< class T, class... Types >
constexpr std::add_pointer_t<const T> get_if( std::variant<Types...>* pv ) noexcept;

This function accepts a pointer to a std::variant object and attempts to extract a value of type T from it. If the extraction is successful, a pointer to the extracted value is returned, otherwise nullptr is returned.

Here is an example demonstrating how to use the std::get_if function:

#include <iostream>
#include <variant>

int main() {
    
    
    std::variant<int, double, std::string> var = 3.14;

    if (auto pval = std::get_if<double>(&var)) {
    
    
        std::cout << "Double value: " << *pval << std::endl;
    } else if (auto pval = std::get_if<int>(&var)) {
    
    
        std::cout << "Int value: " << *pval << std::endl;
    } else if (auto pval = std::get_if<std::string>(&var)) {
    
    
        std::cout << "String value: " << *pval << std::endl;
    }

    return 0;
}

In this example, the std::variant object var contains a value of type double. By using the std::get_if function, we first try to extract a pointer of type double. Since var actually stores a double value and the extraction is successful, Double value: 3.14 will be printed. If a value of another type is stored in var, the corresponding get_if branch will not match and the condition of that branch will be ignored.

It should be noted that the std::get_if function can only extract pointers to constant values, so the return type is std::add_pointer_t, which is a pointer to const T. If you want to modify the value stored in std::variant, you can use the std::get function.

std::any_of()

This STL algorithm is useful when you have a range of elements and want to check whether any given element in the range satisfies a given condition.

Reference blog: https://www.cnblogs.com/Galesaur-wcy/p/15589922.html

TRACE_EVENT_SCOPE

TRACE_EVENT_SCOPE is a macro used for performance analysis and debugging. It is part of the Chrome browser and is also used by many C++ applications and frameworks. This macro can be used to measure the execution time of a block of code and generate event tracing data for analysis and visualization in performance analysis tools.

The usage of the TRACE_EVENT_SCOPE macro is as follows:

TRACE_EVENT_SCOPE(category, name)
  • Category is a classification of events, used to organize and filter event data. You can usually use custom category names, such as "planner", "rendering", etc.

  • name is the name of the event, used to identify a specific code block or operation.

  • When you use the TRACE_EVENT_SCOPE macro, it inserts an event tracking data point at the beginning of the code block and automatically closes the event tracking data point at the end of the code block. By measuring the time difference between these two data points, you can get the execution time of the code block.

  • Event tracking data is typically captured and analyzed by performance analysis tools such as Chrome’s developer tools. These tools can visualize information such as the timestamp, duration, classification, and name of an event so that developers can analyze the code's performance characteristics and bottlenecks.

Before using the TRACE_EVENT_SCOPE macro, you need to include the corresponding header file in the code, such as:

#include "base/trace_event/trace_event.h"

Using {} inside a c++ function does not require adding a semicolon

for example:

void foo() {
    
    
  {
    
    
    // code block 1
  }

  {
    
    
    // code block 2
  }
}

There is no need to add a semicolon after the } at the end of each code block.

Guess you like

Origin blog.csdn.net/qq_40145095/article/details/130100061