默认参数和前向声明

Default parameters and forward declaration

我有一个 class Property 和一个我想要默认参数的构造函数,在文件 property.h:

class Property {
  Property(OtherClass* value = myApp->someValue) {...};
};

其中另一种类型 ApplicationmyApp 是在广泛使用此 属性 class 的另一个文件中定义的。由于这个其他文件#includes property.h,当然,我不能#include 这个文件在property.h

property.h 不知道 myAppApplication(尽管 property.cpp 知道,并且 OtherClassproperty.h 中已知)。 我可以转发声明 OtherClass 并将 myApp 声明为 extern,但这会导致错误 C2027“使用未定义类型的应用程序”,正如预期的那样:

class Application;
extern Application* myApp;
class Property {
  Property(OtherClass* value = myApp->someValue) {...};
};

一个解决方案可能是在 .cpp 文件中使用默认参数,但这是不可取的 (here)。

如何让这个默认参数起作用? (即,不是另一种解决方案不涉及没有默认参数告诉我默认参数是邪恶的,或者我的设计开始时很糟糕等;))。谢谢!

值得注意的是,函数参数的默认参数在调用函数的地方解析(与定义函数的位置相反)。这给出了必要的 space 来解决 OPs 问题 yet another level of indirection:

代替访问myApp->someValue,引入了一个辅助函数,可以在Property定义之前声明,但在Application完全定义之后实现。 (这也可以是 Propertystatic 成员函数。)

演示示例:

#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';
}

输出:

prop1: 123
prop2: 234

Demo on coliru

为了强调 value = getAppDefault() 实际上是在调用构造函数 Property::Property() 时解析的,我在默认构造 prop2 之前修改了 Application::valueDefault


当然,如果 Property 存储的是指针而不是值本身,这也适用:

Demo on coliru

首先,要使用 Application class 的成员,您 必须 具有 Application class。否则编译器将不知道 class.

的成员

其次,您可能应该使 Property 构造函数 explicit 禁止从 Application 进行隐式转换。这种隐式转换往往会使代码更难阅读、理解和维护。

第三,作为解决第一个问题的一种可能方法,您可以使用 重载 来拥有两个 Property 构造函数:一个具有非默认 Property* 参数,还有一个根本没有任何参数。无参数构造函数委托给单参数构造函数进行初始化。这种拆分成两个构造函数将使定义(实现)与声明分开变得更容易。


在代码中,我的解决方案如下所示:

首先是Property头文件

// Forward declaration only, no header file needed
class OtherClass;

class Property
{
public:
    Property();
    explicit Property(OtherClass* other);

    // ...
};

然后Propertyclass文件

#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...
}