c++模块不使用前向声明编译

t30tvxxf  于 2023-05-02  发布在  其他
关注(0)|答案(1)|浏览(121)

我有一个状态管理器,它应该管理从基本状态(如游戏状态,菜单状态等)派生的对象的unique_ptr-smartpointer。然而,基本状态需要能够访问来自状态管理器的功能,这可能导致循环依赖性。使用Forward-Declarations和普通头文件,可以通过以下方法避免这种情况,该方法工作正常:

使用Headers和Forward声明实现

// StateManager.hpp

#ifndef STATEMANAGER_HPP
#define STATEMANAGER_HPP

#include <vector>
#include <memory>

// receive all information about BaseState, so it can be used with unique_ptr
#include "BaseState.hpp" 

class StateManager {
public:
    StateManager() {
        m_states.emplace_back(std::make_unique<BaseState>(this));
    }
    ~StateManager() = default; 
    int provideImportantDataToStates();

private: 
    std::vector<std::unique_ptr<BaseState>> m_states;
};
    
#endif // STATEMANAGER_HPP
// BaseState.hpp
#ifndef BASESTATE_HPP
#define BASESTATE_HPP

class StateManager; // just forward declare StateManager to avoid cyclic dependency
export class BaseState {
public:
    BaseState(StateManager* l_stateManager);
    virtual BaseState() = default; 
};

#endif // BASESTATE_HPP

现在,我可以将StateManager包含在BaseState的实现文件(BaseState.cpp)并访问其功能:

//BaseState.cpp

#include "BaseState.hpp"
#include "StateManager.hpp"

BaseState::BaseState(StateManager* l_stateManager) {
    l_stateManager->provideImportantDataToStates();
}

使用无法编译的模块实现

不幸的是,这种方法似乎与模块不兼容。
下面是完全相同的模块实现,拒绝编译:

// StateManager.ixx
export module StateManager;

import <vector>;
import <memory>;
import BaseState; 

export class StateManager {
public:
    StateManager() {
        m_states.emplace_back(std::make_unique<BaseState>(this));
    }
    ~StateManager() = default; 
    int provideImportantDataToStates() {
        return 5; 
    }
private: 
    std::vector<std::unique_ptr<BaseState>> m_states;
};
// BaseState.ixx
export module BaseState;

class StateManager;
export class BaseState {
public:
    BaseState(StateManager* l_stateManager);
};
// BaseState.cpp
import BaseState;
import StateManager;

BaseState::BaseState(StateManager* l_stateManager) {

    l_stateManager->provideImportantDataToStates();
}

编译器会报告错误消息:

  • 错误(活动)“StateManager”的E3344模块文件Map无效[。..] BaseState。CPP3
  • 错误(活动)E0393指向不完整类类型的指针“StateManager”是不允许的BaseState。CPP7
  • 错误C2665“BaseState::BaseState”:没有重载函数可以转换所有参数类型3393

我的猜测是编译器在前向声明的“类StateManager;”在BaseState中。ixx和“真实的”(即StateManager中的完整StateManager类)。前向声明的StateManager似乎“覆盖”了BaseState中包含的完整的StateManager。使用命令“#include“StateManager调用cpp。hpp”

我不喜欢的可能解决方案

我发现的唯一可行的解决方案是下面的一个,我不认为它看起来特别好。另外,我对将StateManager和BaseState耦合到同一个模块接口单元中感觉不太好,因为它们在逻辑上是不同的东西。

// StateManager.ixx
export module StateManager;

import <vector>;
import <memory>;

class StateManager; 
export class BaseState {
public:
     BaseState(StateManager* l_stateManager);
    virtual ~BaseState() = default; 
};

export class StateManager {
public:
    StateManager() {
        m_states.emplace_back(std::make_unique<BaseState>());
    }
    ~StateManager() = default; 
    int provideImportantDataToStates();
private: 
    std::vector<std::unique_ptr<BaseState>> m_states;
};

BaseState::BaseState(StateManager* l_stateManager) {
    l_stateManager->provideImportantDataToStates();
}

这不可能是C++编程的“新方式”。...所以我想我仍然使用模块不正确的方式我所描述的情况。我做错了什么?

tf7tbtn2

tf7tbtn21#

模块并不意味着像头一样使用。驱动您将逻辑相关的代码拆分为不同头文件的因素对于模块来说并不存在。因此,模块Map到库,而不是库的单个头文件组件。
也就是说,无论StateManagerBaseState是什么,它们在逻辑上都是同一个库的一部分,因此应该是同一个模块的一部分。
然而,从组织的Angular 来看,能够将代码放入不同的文件中可能是有用的,即使它们是同一库的一部分。
C++模块来自单个文件:该模块的主接口单元。然而,该模块可以由任何数量的接口分区单元组成,这些接口分区单元必须直接或间接地由主接口单元包括。
因此,不是将每个类放入它们自己的模块中,而是将它们放入它们自己的模块分区中:

// StateManager.ixx
export module MyLibrary:StateManager; //Partition named StateManager

import <vector>;
import <memory>;
export import :BaseState; //Imports the BaseState partition

export class StateManager {
public:
    StateManager() {
        m_states.emplace_back(std::make_unique<BaseState>(this));
    }
    ~StateManager() = default;

    int provideImportantDataToStates() {
        return 5; 
    }
private: 
    std::vector<std::unique_ptr<BaseState>> m_states;
};
// BaseState.ixx
export module MyLibrary:BaseState;

export class StateManager; //Must be exported here.

export class BaseState {
public:
    BaseState(StateManager* l_stateManager);
};
//MyLibrary.ixx
export module MyLibrary;

export import :BaseState;
export import :StateManager;

单独的模块代码不起作用的原因是因为StateManager的前向声明在StateManager模块的权限范围内。因此,当BaseState模块声明StateManager类时,它并没有声明与StateManager相同的类。属于一个模块的东西不能被重新声明为属于另一个模块。

相关问题