1

i have a struct "Material" which has a referencetype of Item& Item is a baseclass for many different Materials who can appear in a list. The struct also has an integer variable and also a QString variable. Those two are just give the amount to be used and a String of what type item must be casted back.

struct Material
{
    int amount;
    Item& item;
    QString itemtype;
    Material(int myamount,Item& myitem,QString myitemtype) : amount(myamount),item(myitem),itemtype(myitemtype){}
};

As i have read here: initialize struct contain references to structs

to have a reference sitting inside of a struct one need to define a constructor within the struct. as you can see in the upper Material struct.

Now when i try to retrive the reference of that, like here:

QList<Material> Mats = bp.Materials();
for(int i=0;i<Mats.count();i++)
{
      Item& item = Mats[i].item;
}

i allways get an error like "use of deleted function 'Material& Material::operator=(const Material&)"

I also tried already to define such a operator= but obviously if i try to return the rvalue which is const i can't do this because the lvalue is not const. and if i make a new instance of Material it is going to be an implicit copy constructor.

My Question: What do i miss here?

On the wish to make a reproduceable example, i may got to the problem. I have no solution for my one just yet but an idea what may have caused this:

#include <QCoreApplication>
#include <QList>

struct Kram
{
   QString& text;
   Kram(QString& s): text(s) {}
};



int main(int argc, char *argv[])
{
   QCoreApplication a(argc, argv);

   QString mytext= "Hello, World!";
   Kram k(mytext);

   QList<Kram> liste;
   liste.append(k);

   for(int i=0; i<liste.count();i++)
   {
      QString& anothertext = liste[i].text;
   }

return a.exec();

}

When i compile this he tells me the exact same error message about the "Kram" struct. But this only appears if you do the struct in a Qlist and then try to get the reference inside of the struct out.

just to mention the second error message is in both cases thisone:

C:\Users\Rolf\Documents\Testprojekt\main.cpp:4: Fehler: non-static reference member 'QString& Kram::text', can't use default assignment operator

  • 1
    What does `bp.Materials().values()` return? If it's a reference or const reference then this is the type you have to use for your local variable `Mats`. Otherwise, you're lost. As you have it currently, you copy the `QList`, and this seems to make a deep copy (copying all the list elements as well). – Scheff's Cat Jun 30 '22 at 16:24
  • Who is owning the `Item` instances that you keep references to? – Ted Lyngmo Jun 30 '22 at 16:28
  • Hmm... The doc. for [QList::QList(const QList&)](https://doc.qt.io/qt-5/qlist.html#QList-1) mentions [Implicit Sharing](https://doc.qt.io/qt-5/implicit-sharing.html). So, where does the deep copy come from? That's mysterious... – Scheff's Cat Jun 30 '22 at 16:28
  • 2
    Btw. you cannot write a (sane) copy assignment operator for a `struct` with a reference member. In this case, you have to turn it into a pointer or try to prevent any copy assignment. (Sometimes, this can be intended e.g. for resource management.) – Scheff's Cat Jun 30 '22 at 16:35
  • 2
    At least, I cleared up my mind concerning the implicit sharing... :-) Even with implicit sharing, the `QList` must be able to perform a deep copy (when copy-on-write happens). Hence, there is in fact somewhere in the `QList` a copy assignment used for the elements (of type `Material` in your case). And this doesn't compile as the compiler cannot create one for your `Material` due to the `Material::item` member which is a reference. – Scheff's Cat Jun 30 '22 at 16:39
  • About the first Question. I took up that hint that it might be problaby because of bp.Materials().values() is. This was getting the QList from a QMap and now that i thought of this again this made no sence. so i changed this in the back to a normal QList. I also edited my Question here. So now it is just bp.Materials(). But this did not changed anything. I still get the same error message. – Rolf Hausen Jun 30 '22 at 16:54
  • 1
    please post a [mcve] – 463035818_is_not_an_ai Jun 30 '22 at 16:58
  • References can be dangerous. Even if you write a copy assignment. If, for instance, the thing they reference is in a collection and it gets manipulated in almost any way the reference will no longer be valid and will do weird things that are hard to debug. Be very careful with references. – doug Jun 30 '22 at 17:34
  • As long as you insist to store a reference member variable, copy/move assignment are (practically) not available. Hence, a [QList](https://doc.qt.io/qt-6/qlist.html#details) is probably not an option. (I just read that it uses dynamic arrays internally. Hence, it _must_ be able to move the elements.) Alternatively, you could use a [std::list](https://en.cppreference.com/w/cpp/container/list) (which has weaker requirements to the element type) and create the instances of `Kram` in-place (using [std::list::emplace()](https://en.cppreference.com/w/cpp/container/list/emplace)). – Scheff's Cat Jul 01 '22 at 13:07
  • Though, it's a lot of trouble. Are you _really_ sure you want reference member variables instead of a raw pointer or a smart pointer or just a copy of that thing. Specifically, `QString` provides a kind of sharing internally which makes "plain deep" copies very cheap. – Scheff's Cat Jul 01 '22 at 13:12
  • i honestly do not to insist on references i just used it because some mentioned references are way better than dangling pointers. but i guess in this circumstanse pointers are the better choise then? std:list sounds also as an option. Also i just used a reference just because my real program involves inheritance and so i store the reference to just not lose the data from the derived class object stored as a base class object because the list will contain several different types deriving from that base – Rolf Hausen Jul 01 '22 at 20:11
  • A pointer would be my preference here, as you can still choose to have the constructor take a reference argument if you don't want to accept `nullptr`. Also, the reference member will actually dangle just as much if the referenced object gets destroyed prematurely! It sounds like what you want is `std::unique_ptr`, which owns the item data and can also store derived types. Regular pointers and references should only be used to refer to other objects without taking ownership of them. (With the exception, in Qt, of `QObject`s that you parent to other `QObject`s!) – sigma Jul 01 '22 at 21:14

1 Answers1

1

For all who stumble on this, i will post my fixed example code. But i highly recommend to read the comments from Scheff's Cat and sigma below my question. Because they give you some really important infos why this is happening. I my self will consider the idea of sigma to use the std::unique_ptr for my code.

eyeball in the following code that now the struct holds a pointer but the constructer gets a reference so that there is no nullpointer. but still the object here in the main code can be assigned normaly meaning no pointer involved there.

here is the code:

#include <QCoreApplication>
#include <QList>

struct Kram
{
   QString* text;
   Kram(QString& s): text(&s) {}
};



int main(int argc, char *argv[])
{
   QCoreApplication a(argc, argv);

   QString mytext= "Hello, World!";
   Kram k(mytext);

   QList<Kram> liste;
   liste.append(k);

   for(int i=0; i<liste.count();i++)
   {
      QString* anothertext = liste[i].text;
   }

   return a.exec();
}

thank you all for helping me finding out of this maze