This one has annoyed me for a while until I found the answer as pre STL stack classes used to have a pop()
function that was popping the element and returning it to the user. I was finding it conveniant because everytime I wanted to access the top element, I also wanted to pop out the element of the stack so why STL containers is forcing its users to perform this common operation with 2 operations?
In 'The C++ Programming Language' book, Bjarne Stroustrup has this to say about pop_back:
Note that
pop_back()
does not return a value. It just pops, and if we want to know what was on the top of the stack before pop, we must look. This happens not to be my favorite style of stack, but it's arguably more efficient and it's the standard.
I wish that he took the time to explain his position about why it is more efficient for pop_back()
to not return the element but I only understand why it was done like that by reading the book Exceptionnal C++ from Herb Sutter.
If you are using a STL container to store pointers, the performance argument disapear but if you store objects, all front()
is returning is a reference to the top element and if pop_front()
would return the top element, it would be by value and the copy constructor and the assignement operator would be involved. This is the performance argument that by having to call these 2 functions, it is adding overhead.
However, I suspect that this is not the main reason to have separated the 2 operations. Probably that the main reason is for exception safety garantee that the STL containers provide. Since, one requirement that STL impose on its users that contained objects destructors cannot throw exceptions, pop_front()
can provide a no throw garantee. It could not if it had to return the top element because if the copy constructor or the assignment operator was throwing an exception, the element would be lost forever. In contrast, when an exception is thrown while assigning front()
result, if you can cope with the exception, the element is not lost because it is still in the container.
One usage of them commonly demonstrated in C++ Templates textbooks such as C++ Templates is when you want to control the maximum capacity of a container like this:
template< typename T, int MAXSIZE > class stack { ... private: T m_array[MAXSIZE]; };
I have found another very useful property of integral constant template parameters. You can define a class template with an integral constant template parameter and not use it at all in the declaration:
template<int i> class Unique { public: static int var; }; template<int i> int Unique<i>::var;
The property of this template is that a distinct var variable will be created for each instantiation of the template:
Unique<1234>::var = 1; Unique<0>::var = 2;
and so on. By itself, it is more or less useful but imagine that you are dealing with a function registering a callback function that does not let you provide a parameter that will be passed to the callback function but your callback needs to access variables and you must provide more than 1 callback function and each of them needs to access its own set of variables. You could do it by hand by writing x different callbacks and create x sets of variables with different names but by doing that, you would be polluting your namespace and that would be a very long task for nothing. Instead, a class template with an integral constant parameter is the right tool for this situation:
template<int i> class CB { public: static void SetCBParams(int x1, int x2) { m_x1 = x1; m_x2 = x2; } static void CBFunc(); private: static int m_x1; static int m_x2; }; template<int i> int CB<i>::m_x1; template<int i> int CB<i>::m_x2;
and then
CB<1>::SetParam(1,2); registerCB(&CB<1>::CBFunc); CB<2>::SetParam(3,4); registerCB(&CB<2>::CBFunc); ...
Sometimes efficient memory management is needed and to accommodate that need, the C++ language provides the placement new operator to let the user specify the location where an object is going to be instantiated. When the placement new operator is used, the user must then explicitly call the destructor.
This is all fine but I have been wondering what happens when the placement new operator is used in conjunction with templates such as in the std::vector container and when the template parameter is a scalar built-in type that obviously does not have a destructor.
I have found out that the C++ standard committee envisaged the situation and has included specification of what a compiler must do in such situation. I do not have a copy of the standard text but the section 5.2.4 (Pseudo destructor call) states that the expression p->~T()
becomes a pseudo destructor call for a built-in pointer type, which does nothing.
Here is a small program that I have successfully compiled to verify that pseudo destruction works fine:
#include <iostream>
// The header file containing the declaration of the placement new operator
// according to 'The C++ Programming Language' book (section 10.4.11) but it
// compiles fine without. It must be because it is included from <iostream>
#include <new>
class TestClass { public: TestClass() : m_x(3) {} ~TestClass() { std::cout << "TestClass destructor called" << std::endl; } void print() const { std::cout << "Hello " << m_x << std::endl; } private: int m_x; }; template<typename T> class PseudoTest { public: PseudoTest() { m_pObj = new(m_buf) T; } ~PseudoTest() { m_pObj->~T(); } T *m_pObj; private: char m_buf[sizeof(T)]; }; int main(int argc, char *argv[]) { // First test the int case { PseudoTest<int> a; *a.m_pObj = 777; std::cout << *a.m_pObj << std::endl; } // Then if something prints out, that means that the // int pseudo-destructor has been called successfully. { PseudoTest<TestClass> b; b.m_pObj->print(); } return 0; }
One final word, note that I have used the placement new operator on a char buffer located on the stack and, according to Herb Sutter, this is a very bad thing because this generates non portable code. He explains why in his book Exceptionnal C++ (item 30: The "Fast Pimpl" Idiom) and then presents the right way to do it but since my test program compiles and runs fine, my evil code is will be fine for the demonstration purpose of this blog entry.
Question:
What is wrong in this code from the book 'The C++ Programming Language', section 3.7.2? and why does MSVC accept it and not GCC?
// Range checking vector template< class T > class Vec : public std::vector< T > { public: Vec () : std::vector< T > () { } Vec (int s) : std::vector< T > (s) { } // Range checking by using the at () method, // which throws out_of_range T& operator[] (int i) { return at (i); } const T& operator[] (int i) const { return at (i); } };
GCC reports this error:
since
at()
doesn't depend on any template parameters it should be available
Answer:
A compliant compiler will partially validate the template code that does not depend on the template parameters and complete the validation at the template instantiation point. It is that way because code depending on template parameter could have a totally different meaning if between the template declaration and the Point of instantiation (POI) there is a template specialization.
When you call a member function from a template base class, the way it is written is making the name nondependent. To fix the code, 2 options are available. You can either write:
1. is preferred as it will work as expected if the function called is virtual.
If VC++ does not complain, it is because it simply does not support the two phase lookup and compile templates only at instantiation point. GCC is a better compiler in that aspect because it complies with the C++ standard by implementing the Two-Phase lookup. You can easily fool VC++ with the following code:
template <class T> class Test { public: void Test() { foo(); } private: T m_a; } // Non dependent function declared after // template declaration int foo(); int main(int argc, char *argv[]) { Test<int> testObj; testObj.Test(); return 0; }
GCC will generate an error when encountering the template complaining that foo()
is not declared but VC++ will happily accept this code. Now that this is explained, note that except annoyance when porting code from VC++ to GCC or any other standard compliant compiler, the only consequence for MSVC of not being totally C++ standard compliant by not supporting the Two-Phase lookup is that if there are errors in a template, the compiler will delay their report at its instantiation instead of reporting them as soon as the template is encountered.
If you would like to explore further Dependant base classes, Point of Instantiation (POI) and Two-Phase lookup, I highly recommend to consult the book C++ Templates.
This book does a nice job for describing the pthread API. When I have read this book, my multithread programming experience was mainly with Win32 threads and reading this book was my first exposure to the condition synchronization objects. With the help of this book, it has been a breeze to learn how to use conditions. What is missing from this book written 10 years ago, which is also missing in all multithread books that I have read of that era, is coverage on issues with parallel processing. If all you have to do with threads is to launch a background job while keeping UI responsive or asynchronous I/O on a single core processor, you will be fine with this book.
However, if you try to crunch an array of data with multiple threads each processing their own chunk of the array, you could fall into cache line alignment problems even if your threads does not access the same memory locations. Those problems are platform dependant. I have written such a program that was working wonderfully well with a Sparc station and a PowerPC based station but once ported to a x86 architecture, the program was actually becoming slower than the single thread version. It is very hard to get it right. You have to be careful about the array alignment in memory and where the boundaries of the chunks of data that you assign to threads are. What will happen if 2 threads located on 2 different processors access to the same cache line is that one processor will have to flush that cache line back to the main memory and the other processor will have to fetch the values back from the main memory to its cache. The overhead of this is so huge that processing the array from a single thread could be faster.
I still have to find a book that addresses these problems. I expect it to come soon with dual and quad core processors becoming mainstream but this is not this book.
Not all of the 44 tips are exceptional. Some of them are pretty trivial such as "Read Stevens books" or "consult RFCs" but about 35 tips are very good. The author knows well this topic and explains very well the reasons behind these tips. I am sure that all these good tips can be found in the TCP/IP Illustrated books but if you do not have the time to read 3 volumes consisting of about 2000 pages, this less than 300 pages book will provide a nice synthesis of TCP/IP programming good practices.
I had to do some text processing and I wanted to learn sed & awk so I picked this book that is, to my knowledge, the only book completely dedicated to these tools. The chapter on 'advanced' sed programming is very scary because when using 'advanced' sed features, the syntax is cryptic. It is nice to know that these features are there but I want to stay away from them and perhaps use awk or perl for the tasks that would require those 'advanced' features. Still, when you stick with the 'basic' and 'intermediate' sed features, you can do cool things easily in shell scripts.
Overall, the book was not thrilling to read but it does a good job to teach you these text processing tools.
By having read the 3 books in the serie, I must say that this is my favorite because it has really answered a lot of small questions I had for a long time about several aspects of C++.
The only thing that I have disliked is that Mr. Sutter has contradicted himself with one of the advice that he was giving in the first book. The advice was to not write an assignment operator in terms of its copy constructor with the in place new operator and the explicit destructor and I did agree with the reasoning. Now in this book, it is written that is ok to use that idiom if and if and if and he goes for about a paragraph to describe the condition where it is ok to use the pattern. I can imagine that the author had to admit that there might be exceptional situation where it is ok to use that technique in a heated debate but my opinion is that out of context, he should not have mention that it might be ok to use that technique without devoting an entire item on that topic. It did just confuse me and I had to reread the item in the first book to convince me that there was effectively a contradiction between the 2 books and that it was not just my memory playing games with me.
I want you to find in this blog informations about C++ programming that I had a hard time to find in the first place on the web.
Sun | Mon | Tue | Wed | Thu | Fri | Sat |
---|---|---|---|---|---|---|
<< < | Current | > >> | ||||
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |