I have a class Property
with a constructor in which I want default parameters, in a file property.h
:
class Property {
Property(OtherClass* value = myApp->someValue) {...};
};
where myApp
, of another type Application
, is defined in another file that makes extensive use of this Property class. Since this other file #includes property.h, of course, I cannot #include this file in property.h
property.h
does not know about myApp
nor Application
(though property.cpp
does, and OtherClass
is known in property.h
).
I could forward declare OtherClass
and declare myApp
as extern
, but this results in an error C2027 "use of undefined type Application", as expected :
class Application;
extern Application* myApp;
class Property {
Property(OtherClass* value = myApp->someValue) {...};
};
A solution could be to have the default parameter in the .cpp file, but this is not advisable (here).
How can I get this default parameter working ? (i.e., not another solution that would involve not having default parameters telling me default parameters are evil, or that my design is poor to start with etc. ;) ). Thanks!
CodePudding user response:
First of all, to use a member of the Application
class you must have the full definition of the Application
class. Otherwise the compiler will not know about the members of the class.
Secondly, you should probably make the Property
constructor explicit
to inhibit implicit conversions from Application
. Such implicit conversion tend to make code harder to read, understand and maintain.
Thirdly, and as a possible way to work around the first problem, you could use overloading to have two Property
constructors: One with a non-default Property*
argument, and one without any argument at all. The argument-less constructor delegates to the one-argument constructor for initialization. This split into two constructor function will make it easier to define (implement) separate from declaration.
In code my solution would look something like this:
First the Property
header file
// Forward declaration only, no header file needed
class OtherClass;
class Property
{
public:
Property();
explicit Property(OtherClass* other);
// ...
};
Then the Property
class source file
#include "Property.h"
// Get the full and complete definition of the Application class
#include "Application.h"
extern Application* myApp;
// Will probably need the full definition of the OtherClass as well
#include "OtherClass.h"
// Default constructor delegates to one-argument constructor
Property::Property()
: Property{ myApp->GetOtherObject() }
{
}
Property::Property(OtherClass* other)
{
// Use the other class here...
}
CodePudding user response:
The remarkable point is that the default argument of function parameters is resolved where the function is called (in opposition to where the function is defined). That gives the necessary space to solve OPs problem by yet another level of indirection:
Instead of accessing myApp->someValue
, a helper function is introduced, which can be declared before the definition of Property
but implemented after Application
is fully defined. (This could be a static
member function of Property
as well.)
An example to demonstrate:
#include <iostream>
int getAppDefault();
struct Application;
extern Application *pApp;
struct Property {
int value;
Property(int value = getAppDefault()): value(value) { }
};
struct Application {
int valueDefault = 123;
};
Application app;
Application *pApp = &app;
int getAppDefault() { return pApp->valueDefault; }
int main()
{
Property prop1;
std::cout << "prop1: " << prop1.value << '\n';
app.valueDefault = 234;
Property prop2;
std::cout << "prop2: " << prop2.value << '\n';
}
Output:
prop1: 123
prop2: 234
To emphasize that value = getAppDefault()
is in fact resolved when the constructor Property::Property()
is called, I modified the Application::valueDefault
before default-constructing prop2
.
And, of course, this works as well if the Property
stores a pointer instead of the value itself: