C++ 是否可以将前向声明和常规声明合并到一个文件中,然后像使用';我们分开了?
我有一个疯狂的想法,通过执行一些宏欺骗,将前向声明头和实际的声明文件合并成一个。为了提供一些上下文,我对转发声明遵循的每日策略如下:C++ 是否可以将前向声明和常规声明合并到一个文件中,然后像使用';我们分开了?,c++,c,c-preprocessor,C++,C,C Preprocessor,我有一个疯狂的想法,通过执行一些宏欺骗,将前向声明头和实际的声明文件合并成一个。为了提供一些上下文,我对转发声明遵循的每日策略如下: 每个头文件都有其“_fwd.hpp”补码,其中包含该头文件中所有可向前声明实体的向前声明 我包括转发声明头,其中转发声明实际内容就足够了 我主要将常规声明头包含在.cpp文件中,并且仅当需要实际的实现信息时(需要实现大小、继承等) 但是,为每个集管配备单独的_fwd.hpp集管会污染项目,并且难以维护。因此,我提出了以下想法:将前向声明和实际声明合并到单个文件
- 每个头文件都有其“_fwd.hpp”补码,其中包含该头文件中所有可向前声明实体的向前声明
- 我包括转发声明头,其中转发声明实际内容就足够了
- 我主要将常规声明头包含在.cpp文件中,并且仅当需要实际的实现信息时(需要实现大小、继承等)
#if !defined(FOO_FWD_H)
#define FOO_FWD_H
// Forward declarations goes here
struct foo;
#else // has forward_declaration, include actual if not included yet
#if !defined(FOO_H)
#define FOO_H
struct foo{
foo(){/*....*/}
};
// Normal declarations goes here
#endif // FOO_H
#endif // FOO_FWD_H
如果我包含“foo.hpp”一次,我会得到foo的前向声明,但是如果我在翻译单元中包含第二次,我会得到foo的前向和实际声明,这对我来说是完全正确的。(反正我也在做同样的事情,在页眉中包括fwdecl,在cpp中包括actual)
所以,在上面描述的用例中,它是这样的
bar.hpp
#pragma once
#include "foo.hpp" // forward declaration
struct bar{
bar(const foo& f);
};
#include "bar.hpp" // bar + 1st inclusion of foo.hpp
#include "foo.hpp" // included 2nd time, actual implementation enabled
bar::bar(const foo& f){
f.rab(); // requires actual implementation
}
#if !defined(FOO_FWD_HPP)
#define FOO_FWD_HPP
// forward declarations go here
struct foo;
#endif // FOO_FWD_HPP
#if !defined(FOO_FWD_ONLY)
// includer wants the full monty
#if !defined(FOO_HPP)
#define FOO_HPP
// normal declarations go here
struct foo{
foo(){/*....*/}
};
#endif // FOO_HPP
#endif // FOO_FWD_ONLY
#undef FOO_FWD_ONLY
#pragma once
// only need forward declarations from foo.hpp
#define FOO_FWD_ONLY
#include "foo.hpp"
struct bar {
bar(const foo& f);
};
#include "bar.hpp"
#include "foo.hpp"
bar::bar(const foo& f){
f.rab(); // requires actual implementation
}
bar.cpp
#pragma once
#include "foo.hpp" // forward declaration
struct bar{
bar(const foo& f);
};
#include "bar.hpp" // bar + 1st inclusion of foo.hpp
#include "foo.hpp" // included 2nd time, actual implementation enabled
bar::bar(const foo& f){
f.rab(); // requires actual implementation
}
#if !defined(FOO_FWD_HPP)
#define FOO_FWD_HPP
// forward declarations go here
struct foo;
#endif // FOO_FWD_HPP
#if !defined(FOO_FWD_ONLY)
// includer wants the full monty
#if !defined(FOO_HPP)
#define FOO_HPP
// normal declarations go here
struct foo{
foo(){/*....*/}
};
#endif // FOO_HPP
#endif // FOO_FWD_ONLY
#undef FOO_FWD_ONLY
#pragma once
// only need forward declarations from foo.hpp
#define FOO_FWD_ONLY
#include "foo.hpp"
struct bar {
bar(const foo& f);
};
#include "bar.hpp"
#include "foo.hpp"
bar::bar(const foo& f){
f.rab(); // requires actual implementation
}
但正如你所能想象的,这种方法存在问题。最大的问题是,如果另一个标题tar.hpp包含foo.hpp,而tar.hpp包含在bar.hpp中,则会导致实际实现暴露在bar.hpp中,从而无法实现此目的。此外,当bar.hpp中需要实际实现foo.hpp时,它必须包含两次,这看起来很奇怪(linter和像iwyu这样的工具可能会有问题)
所以问题归根结底是这样的,我们真的能以这样一种方式使它工作吗
- 包含使用此习惯用法的标头不会干扰其他标头的包含状态
- 在需要实际实施时消除双重包含的需要
/(mgilor):我们有很多样板代码,
//也许x-macro的概念可以帮助我们摆脱样板文件,转而使用
//单独的文件?
#如果已定义(仅限FOO_FWD_)
#undef FOO_FWD_HPP//防止在其他头上意外包含实现
#恩迪夫
#如果已定义(仅限FOO_FWD_)&&!已定义(FOO_FWD_水电站)
#定义FOO_FWD_水电站
//转发声明到这里
结构foo;
#艾利夫!已定义(仅限FOO_FWD_)
//includer想要一个完整的月
#如果!已定义(福乌水电站)
#定义FOO_水电站
//实际声明在这里
结构foo{
foo(){/*....*/}
void do_things(){}
};
#endif//FOO_水电站
#endif//FOO_FWD_水电站
//取消定义宏,使未来包含不会受到影响
#仅适用于未定义的FOO_FWD_
焦油hpp(仅限fwdecl食品消费者)
#pragma一次
#仅定义FOO_FWD_
#包括“foo.hpp”//此标题需要转发声明
#ifdef FOO_FWD_水电站
#pragma消息(_文件_“具有foo的前向声明”)
#恩迪夫
#ifdef FOO_水电站
#pragma消息(_文件_“具有完整的foo声明”)
#恩迪夫
结构焦油{
焦油(foo&f){}
};
bar.hpp(fwdecl仅为foo的消费者,也消耗tar.hpp)
#pragma一次
#仅包括“焦油量hpp”//foo fwdecl消耗的焦油量
#仅定义FOO_FWD_
#仅包括“foo.hpp”//bar需要fwdecl
#ifdef FOO_FWD_水电站
#pragma消息(_文件_“具有foo的前向声明”)
#恩迪夫
#ifdef FOO_水电站
#pragma消息(_文件_“具有完整的foo声明”)
#恩迪夫
结构条{
酒吧(富富);;
};
酒吧cpp(酒吧和美食的12月份消费者)
#包括“bar.hpp”
#包含“foo.hpp”//second包含,应启用完整定义
#ifdef FOO_FWD_水电站
#pragma消息(_文件_“具有foo的前向声明”)
#恩迪夫
#ifdef FOO_水电站
#pragma消息(_文件_“具有完整的foo声明”)
#恩迪夫
酒吧:酒吧(foo&ref){
参考:做事;
}
baz.hpp(无相关性)
#pragma一次
结构baz{
void do_baz();
};
baz.cpp(foo&baz的12月份消费者)
#包括“baz.hpp”
#包括“foo.hpp”//foo之前不包括,但因为没有定义foo_FWD_ONLY
//baz.cpp将获得完整声明。
#ifdef FOO_FWD_水电站
#pragma消息(_文件_“具有foo的前向声明”)
#恩迪夫
#ifdef FOO_水电站
#pragma消息(_文件_“具有完整的foo声明”)
#恩迪夫
void baz::do_baz(){
福福;
f、 做事();//完全好。
}
main.cpp(消费应用程序)
//消费应用程序
#包括“tar.hpp”
#包括“bar.hpp”
#include“foo.hpp”//已经有以前的foo-fwdecl,所以第二次包含将启用完整声明。
//(此外,FOO_FWD_ONLY未定义,因此首次包含也将启用它)
#包括“baz.hpp”
内部主(空){
福福;
焦油t(f);
b栏(f);
baz-bz;
}
编译时的输出:
tar.hpp:7:13: warning: tar.hpp has forward declaration of foo
bar.hpp:8:13: warning: bar.hpp has forward declaration of foo
bar.cpp:6:13: warning: bar.cpp has forward declaration of foo
bar.cpp:9:13: warning: bar.cpp has full declaration of foo
baz.cpp:9:13: warning: baz.cpp has full declaration of foo
tar.hpp:7:13: warning: tar.hpp has forward declaration of foo
bar.hpp:8:13: warning: bar.hpp has forward declaration of foo
这是一个