Friday, August 14, 2009

Baffled by templated operator=

Now this really took me by surprise and I just believed it after extensive debugging. Here's the punchline: A templated operator= is not used for assignments from the same data-type. That means when test has a templated operator=, this does not call it:

test a, b;
a = b;


On a second thought, this is actually comprehensible behaviour. The compiler generates a default operator= for assignments of it's own type that calls operator= for each member. This default operator is even generated when a templated operator exists. And because overloaded functions have stronger "binding" than template-functions, the implicitly created and therefore overloading operator= is called. Here's an example:

#include <iostream>

struct test
{
template<typename T>
test &operator=(const T &value)
{
std::cout << "template operator=()" << std::endl;
return *this;
}
};

int main()
{
test a, b;
b = 10;
a = b;
}

You might think that the output is
operator=()
operator=()

but it isn't. The output contains only one line, because the generated operator= does not create any output.


It is extremely important to know this rule and don't fall into it's trap like I did. In my case I had a pointer-member and simply assigning it resulted in a double-free in the dtor of the last destroyed copy of that object. Here's the correct way to circumvent this:

#include <iostream>

struct test
{
template<typename T>
test &operator=(const T &value)
{
std::cout << "template operator=()" << std::endl;
return *this;
}
test &operator=(const test &value)
{
return operator=<test>(value);
}
};

int main()
{
test a, b;
b = 10;
a = b;
}

Thursday, August 13, 2009

Shrinking Qt widgets to minimum needed size

Shrinking a Qt widgets to the minimum size they can become because of size constraints or child widgets is not a difficult task. In fact, it's simply one call to resize(0, 0) that does just that. Because even resizes by the programmer are bound to the usual constraints, this resizes to the smallest possible size.


But when trying this in practical situations, it might become tricky. Shrinking your widgets means it becomes smaller. A widget (or dialog, or window) usually becomes smaller after removing or changing stuff. You will notice, however, that after removing a widget, a call to resize(0, 0) will not resize your widget to the size it would occupy without the removed widget, but instead it behaves like the widget was still there. This is because the widget is not completely removed until the next processing of the message loop. There are two ways to make certain that the message loop is being processed. One is calling QApplication::processEvents(). The other is using a QTimer::singleShot() and doing the task you want to do in the provided slot. I've tried both in regards to shrinking the widget and noticed that using a singleShot is more flicker-free than calling processEvents(), at least on the Windows platform.


I've created a demo-application to visualize the problem and check which method works better on a certain platform. Here are some screenshots:

Initial window
This is the window after opening the application. You can choose between directly shrinking the widget after removing the last button, using a singleShot() and using processEvents().


Added two buttons
Two buttons have been added, the dialog has automatically grown.


Removed button, shrinked directly
The last button has been removed, resize(0, 0) has been called directly after removing the widget. You can see that the size is not appropriate, the space the button took is still there. Notice the wider gaps between the widgets.


Removed button, shrinked in singleShot
The last button has been removed, resize(0, 0) has been called in a slot that was activated by a QTimer using a singleShot(). The size of the window is correct, no flicker was visible.


Removed button, called processEvents first
The last button has been removed, QApplication::processEvents() has been called before calling resize(0, 0). The size of the window is correct, but there was flicker visible.


You can download the source-code of the demo-appliction here and try it out yourself.