PDA

View Full Version : Qt initializer



j4l
20th September 2011, 10:40
I need one line to initialize a QList

This does not work,


const QList<int> & test(QList<int>() << 7 << 9); //I know, I could simply write const QList<int> test(...); without using & since QList does copy only on write...but....

My reference points to an empty QList. Why? I thought it is equivalent to this:


QList tmp;
tmp << 7;
tmp << 9;
const QList<int> & test(tmp); //this *is* ok



And note that assuming there is a function fn somewhere,


int fn(const QList<int> & list) {return list.size();}

fn(QList<int>() << 7 << 9); //works!! isn't it the same?


Thanks,
J

stampede
20th September 2011, 10:58
Reference is just like a pointer, it needs a valid, existing object to point to. In your case (1) the temporary list gets deleted soon after its created, reference does not "copy" the content of temporary objects. Second case is ok, reference points to an existing object.
Why do you want to use references anyway ?
---
edit:

int fn(const QList<int> & list) {return list.size();}

fn(QList<int>() << 7 << 9); //works!! isn't it the same?
The argument passed to function remains valid until the function returns, note that you cannot use the passed argument after the fn() finishes, because it no longer exists. In your first code snippet the argument passed to reference initializer gets deleted too, but you still want to use it later ( by holding a reference to it ).

j4l
20th September 2011, 11:06
Hi stampede,

Thanks for the answer but I think you are wrong. C++ Standard does allow to keep references to temporaries. A temporary is kept alive as long as there is a reference in scope. E.g, try this



std::vector<int> someFunction()
{
std::vector<int> v;
v.push_back(6);
return v;
}

const std::vector<int> & v = someFunction(); //perfectly valid, temporary is returned and kept alive (const & can always bind to temporaries)


I do this for performance reasons; since returned data is const, why make a copy?

stampede
20th September 2011, 11:13
QList uses copy-on-write semantics, so no copy is made when you assign one list to another. Actual copy is made only when you change one of the objects.

j4l
20th September 2011, 11:20
I know that with QList, that reference is redundant. That's just a habit I got with non-qt code.

Still, to me it seems that the initializer example should work the same way as the function call. I'm still confused.

Thanks,

MarekR22
20th September 2011, 11:44
Men, doesn't your compiler complain (give warnings) that something is wrong? I'm pretty sure it does!
It looks like that you are confusing C++ references with reference types form Java or C#.
This two things are completely different and in C++ temporary automatic objects are allays ceases to exist in next line.
In last example someFunction() returns a value, compiler optimizes that by delivering to this function pointer where result should be created. In you use case of someFunction() first creates temporary object and reference to that is stored (so it is wrong). You are just lucky that it works. If you do something more with this reference you will have a crush.

j4l
20th September 2011, 12:19
nope,

const T & x = T(); //is perfectly valid code in c++

creates a temporary T by calling ctor: T()

then keeps it alive in local scope (where const T & x is defined)


Try it with any C++ (not java) compiler

See http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/

stampede
20th September 2011, 13:06
You are right, but I think those two examples are slightly different. What I meant was that in cases as below:


// (1)
const QList<int> & x = (QList<int>() << 2);
const QList<int> & x2(QList<int>() << 1);

the temporary lists are deleted before the declared reference can "catch" them, but here:


const QList<int>& x = someMethodReturningQListByValue();

the list is kept "alive" until reference goes out of scope, because its returned by value. Same as constructor T(). Try to return the reference (const or not, no matter), compiler will start to complain.
Remember that operators from (1) returns a reference ( are declared as QList<T>& operator<< )
If you create custom operator << and return the List by value, all will work.

FelixB
20th September 2011, 13:07
AFAIK does the scope of a parameter list ends when the called function returns. So, in your case, the list is valid during the copy constructor, but it gets invalid when the constructor returns.

wysota
20th September 2011, 13:48
What is the actual error you are getting when compiling with g++?

j4l
20th September 2011, 14:45
I get no compile error.
It's just that I get a call of the QList dtor and the list I am referencing is empty, as if the two appends (<<) were never performed.
Tried on msvc and gcc as well

wysota
20th September 2011, 15:05
So apparently you are wrong with the thing related to persisting references to temporary objects. From what I understand you need to assign the reference to an object for this to work. The temporary object will persist through its scope but then it has to be copied to something that exists.

j4l
20th September 2011, 15:32
I was wrong assuming that the expression QList() << 7 << 9 returns a temporary.
Yes, QList() creates a temporary, then is gets 7 and 9 appended, which each time a value is appended by calling <<, this call returns QList& which is not a temporary object but a reference to a temporary.
(because QList::operator << return QList&). Note that returned QList& is not a temporary anymore, but a lvalue itself.

Conclusion

1) QList<int> & l1 = QList<int>(); //illegal c++, rvalue (temporary) may only be bound to const&; compilers are smart and warn about this!

2) const QList<int> & l2 = QList<int>(); //perfectly valid, legal c++, const& binds to directly to the temporary/rvalue (list ctor called; no dtor called)

3) QList<int> & l3 = QList<int>() << 7 << 9; //non const reference is dangerously!! bound to the result of the expression QList() << 7 << 9, a reference! to a temporary itself, and temporary is destroyed immediately (dtor is called and I get a dangling reference)

4) const QList<int> & l4 = QList<int>() << 7 << 9; //I was hoping to get const reference bound to the temporary, therefore to keep temporary alive; but in this case, const is useless, bounds to a ref to a temporary and temporary gets destroyed, same as in 3); dtor is called, I get a dangling reference


While 2) is a great option in c++ and 1) is discarded by the compiler, 3) and 4) are similarly dangerous and unfortunately silent. I learned something :-)

Thank you all

wysota
20th September 2011, 16:14
To be honest in this particular case there is no point in using a const reference, you can use a copy-constructor which is usually very cheap.


QList<int> l1 = QList<int>() << 1 << 2;

I think C++0x has some additional optimizations here and it will detect the list gets destroyed after assignment and it will just "move" the data from the temporary list to the l1 object. Or at least the developer of the class will be able to declare such behaviour.

j4l
20th September 2011, 17:20
yes, I know, QList and other qt containers are using copy on write, so this does not really make sense for QList.

But actually I was worried about the idiom itself, and less about QList.

Indeed, c++11 will bring rvalue references as well, but it seems I still have to learn about c++03 in the mean time, at least about subtle things like these.

wysota
20th September 2011, 20:16
yes, I know, QList and other qt containers are using copy on write, so this does not really make sense for QList.

But actually I was worried about the idiom itself, and less about QList.
Actually I meant a general case, not implicitly-shared classes.

j4l
20th September 2011, 22:01
in the general case, I am avoiding a copy (I wanted a single line of code)