简体   繁体   中英

Passing reference to constructor, default argument

I'm creating an own Qt C+ class that is supposed to take a reference to a QCustomPlot from the ui-> as input argument to the constructor.

It should look like this:

myPlots::myPlots(QObject *parent, QCustomPlot& _plot ) :
    QObject(parent), plot(_plot)
{


}

I dont want to create the plot area programmatically as it's neater do to it in the UI edit. And don't wish to use a pointer as argument as references are safer and more syntax clean as I understand. However I get the following error:

"error: default argument missing for parameter 2 of 'myPlots::myPlots(QObject*, QCustomPlot&)' explicit myPlots(QObject *parent=0, QCustomPlot& _plot);"

I tried =0 and =null as default argument but only got errors. What is happening here?

The error in your question is because arguments with default values must be after arguments without default values, those are the rules of C++.


As for having the _plot argument having a default value, it kind of depends on how the plot member variable is declared. If it's not a reference, you can make the _plot argument a reference to a constant instead and have a default-constructed QCustomPlot object as default value:

myPlots(QObject *parent = nullptr, const QCustomPlot& _plot = QCustomPlot(parent));

However, this only works if the plot member is not a reference. If it is a reference, then you should probably have two constructors: One taking a QCustomPlot reference, and one without:

myPlots(QObject *parent = nullptr);
myPlots(QCustomPlot& plot_, QObject *parent = nullptr);

The first constructor needs to create a QCustomPlot instance that is never destroyed during the lifetime of the myPlots instance being constructed, and use that for the reference.

If you want to use default arguments (for functions or templates), there's no way to indicate a skipped argument, you can only leave some off at the end.
Therefore, C++ forces you to provide defaults for any argument following one having such.
If there's no good default for the second but for the first, use multiple constructors or change the arguments ordering.

If you really want to give a default for a l-value reference argument (non-const and non-move), use a global static object.
For other references / non-references, constructing an object just there is ok.

Do not try to force C++ to accept a non-object using cute tricks, if you do you really deserve the Undefined Behavior you get.

Example for static object:

struct myPlots {
    explicit myPlots(QObject *parent = 0, QCustomPlot& _plot = standard_plot);
    static QCustomPlot standard_plot;
}

Example for reordering:

struct myPlots {
    explicit myPlots(QCustomPlot& _plot, QObject *parent = 0);
}

Example for overloads:

struct myPlots {
    myPlots(QObject *parent, QCustomPlot& _plot);
    explicit myPlots(QCustomPlot& _plot);
}

According to the C++ Standard

In a given function declaration, each parameter subsequent to a parameter with a default argument shall have a default argument supplied in this or a previous declaration or shall be a function parameter pack.

and

A reference shall be initialized to refer to a valid object or function. [ Note: in particular, a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the “object” obtained by indirection through a null pointer, which causes undefined behavior.

It is not a good idea to set a reference to zero or NULL directly or indirectly. To be fair, setting it directly will probably not even work because it will fail to compile, and the rest will have undefined behavior.

However, the compiler is trying to help you how to use the constructor:

myPlots::myPlots(QObject *parent, QCustomPlot& _plot ) : QObject(parent), plot(_plot)

In constructors setting parents, you usually leave the parent as the last argument with default null pointer value, or you get an overloaded version with the parent only. Although that is not the main issue in here.

The issue is that you are trying to use a reference without a valid object. You need to initialize references to "valid" objects. You could initialize it to null pointer through a proxy object, this the quote. You could set a const reference to a temporary object, but that would have undefined behavior as the temporary get destructed abruptly.

I would personaly suggest to reconsider your design and use pointer or value semantics. References are good in general, but I do not see the gain of it in this particular case.

So, I would personally write something like this:

header file

explicit myPlots(QCustomPlot _plot, QObject *parent = Q_NULLPTR);

or

explicit myPlots(QCustomPlot *_plot = 0, QObject *parent = Q_NULLPTR);

Before you start asking about Q_NULLPTR in a comment, it is not magical. It is a hidden gem, but it is possible to use it freely as the maintainer said it will not break. It simply falls back to 0 without C++11 or later support, otherwise it is defined to nullptr , so your software will work with both.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM