In January 1995, Scott Meyers in the C + + magazine that "Min,max is a big challenge for the C + + community", he analyzed the Min,max based on macro-based, and compared min based on the template, Max implementation, he got the following conclusion:
"What is the best way to achieve Max?" With a Tevye quote: "I will tell you: I do not know", based on the above analysis, I have the confidence to tell you that the method of macro implementation, perhaps the best, but I personally hate: macros. Therefore, if you have any better way to achieve, please tell me quickly.
According to my personal knowledge, at this time of the day, this challenge still exists, and in this article I will face the challenge, but before I begin, let's recall how the previous generics program was doing poorly in implementing this algorithm.
Since the publication of "Generic<programming>: volatile-multithreaded Programmer ' Best Friend", I've received a lot of emails. I received a lot of awards in these emails, and there were a lot of complaints about the Usenet newsgroups comp.lang.c++ newsgroups and comp.programming.threads. This discussion is more intense, if, you are interested, you can go to attend. The title is: "Volatile, was:memory visibility between threads." A discussion.
In these discussions, I think, I learned a lot of knowledge, here are a lot of independent examples, OK, long story short, many systems will not modify volatile data (such as in POSIX compilation system), while in other systems, add volatile amount, also do not help the quality of the program to improve, The most important question about the correct use of volatile mutexes is that it relies on a POSIX mutexes, while in a multiple-process system, this mutex is often not enough, so you have to use memory protection.
Another more philosophical issue is that strictly speaking, it is unreasonable to volatile rules away from variables, even if you add volatile that match the rules you apply to volatile.
A system can store volatile data in different locations, unlike non-volatile data, so fixing its storage address will make the system unstable. The correctness of volatile is also another object of criticism, although it can be condition at low levels of race, but it cannot detect the logical race condition at a higher level. For example, you have a class mt_vector like std::vector, and there are some synchronized member functions.
As follows:
volatile mt_vector<int> vec;
...
if (!vec.empty()) {
vec.pop_back();
}
The original idea was to remove the last element from the vector of the container, in a single-threaded environment, this code will work well, but if you use such code in multithreading, it is often unusual, even if your empty (), Pop_bock () has been properly synchronized. Thus, it can be said that low-level data consistency is protected, but higher levels of data manipulation are often unstable.
At least, based on the above analysis, I began to stick to my verification volatile method, which is a good way to effectively detect race conditions similar to POSIX mutexes. However, if you are engaged in a multiple process system memory access permission arrangement, then, you should first listen to your compiler documentation. You should know what you should do. Kenneth Chiu mentioned a very interesting paper: http://theory.stanford.edu/~freunds/race.ps, Pape the "type-based Race detection for Java."
Because Java type systems have few limitations, this makes it possible for compilers to work with programmers to detect race conditions at compile time.
Min and Max
Let's take a look at the challenges Scott posed, based on the macro definition of min () as follows:
#define min(a, b) ((a) < (b) ? (a) : (b))
Because it is completely generic, it often works well. (as long as the expression <,? is defined) Unfortunately, min () always calculates one of its intermediate parameters two times, which leads to a lot of confusing problems. If the following call, A=3,b=5 int c=min (A++,B), we will get the result of c=4. Obviously not what we want. A macro definition is just like a function in surface form, but it behaves like a function. (If you want to get more relevant knowledge, you can look up Herb's information about this) in a more efficient implementation of the C + + standard library, which is a template-based solution, as follows:
const T& min(const T& lhs, const T& rhs)
{
return lhs < rhs ? lhs : rhs;
}
You can see that the parameters and return values in this scheme are const, which leads to a problem. Maybe you want to add 2 to the number of the two decimal numbers, so you might say:
double a, b;
...
min(a, b) += 2;
The definition of min () based on a macro does not appear to be problematic, but if the template is implemented on a template it will not be so obedient. Because you can't change the value of a const variable, as Scott noticed. We are here to update our implementation:
T& min(T& lhs, T& rhs)
{
return lhs < rhs ? lhs : rhs;
}