我有一个疯狂的想法,即通过执行一些宏技巧将前进的声明头和实际的声明文件合并为一个。为了提供一些背景信息,我遵循的前向声明(forward declaration)的每日政策如下:

  • 每个头文件都有其“_fwd.hpp”补码,其中包含该头文件中所有可向前声明的实体的正向声明。
  • 我包括前向声明 header ,其中实际事物的前向声明就足以
  • 我主要在.cpp文件中包含常规声明 header ,并且仅在需要实际的实现信息时(需要实现的大小,继承等的事物)才包括在内。

    但是,每个 header 都有单独的_fwd.hpp header 会污染项目,并且很难维护。因此,我提出以下想法:将前向声明和实际声明合并到单个文件中,然后根据包含计数启用它们。我想到了头文件双重包含的最初想法;

    foo.hpp
    #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的前向和实际声明,这对我来说完全可以。 (因为我无论如何都在做同样的事情,所以在 header 中包含fwdecl,在cpp中实际包含)。

    因此,在上述用例中,它是这样的;

    bar.hpp
    #pragma once
    
    #include "foo.hpp" // forward declaration
    
    struct bar{
        bar(const foo& f);
    };
    

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

    但您可以想象,这种方法存在问题。最大的问题是,如果bar.hpp中包含另一个 header tar.hpp和tar.hpp包含的foo.hpp,则会导致实际的实现暴露给bar.hpp,从而达不到目的。另外,当bar.hpp中需要foo.hpp的实际实现时,它必须被包含两次,这看起来很奇怪(像iwyu这样的小工具和工具可能对此有问题)。

    因此,问题归结为这一点,我们是否可以以这种方式实际进行这项工作?
  • 包括 header ,使用该习惯用法不会干扰其他 header 的包含状态
  • 在需要实际实现时消除了双重包含的需要

  • 提前致谢。

    更新:(格林尼治标准时间+30 19/30/10

    基于@IanAbbott的答案的成语的改进版本:

    尝试一下:repl.it

    foo.hpp (我们的单个fwdecl和decl惯用语实现 header )

        // (mgilor): we got ourselves quite a lot boilerplate code,
        // maybe x-macro concept help us to move away boilerplate to
        // a separate file?
    
        #if defined(FOO_FWD_ONLY)
            #undef FOO_FWD_HPP // prevent accidental implementation inclusion on other headers
        #endif
    
        #if defined(FOO_FWD_ONLY) && !defined(FOO_FWD_HPP)
            #define FOO_FWD_HPP
            // forward declarations go here
            struct foo;
        #elif !defined(FOO_FWD_ONLY)
    
             // includer wants the full monty
            #if !defined(FOO_HPP)
                #define FOO_HPP
                // actual declarations go here
    
                struct foo{
                    foo(){/*....*/}
                    void do_things(){}
                };
    
            #endif // FOO_HPP
    
        #endif // FOO_FWD_HPP
    
        // undef the macro, so future includes does not get affected
        #undef FOO_FWD_ONLY
    

    tar.hpp (仅fwdecl的foo使用者)

        #pragma once
    
        #define FOO_FWD_ONLY
        #include "foo.hpp" // this header needs forward declaration
    
        #ifdef FOO_FWD_HPP
            #pragma message ( __FILE__ " has forward declaration of foo")
        #endif
        #ifdef FOO_HPP
            #pragma message ( __FILE__ " has full declaration of foo")
        #endif
    
        struct tar{
            tar(foo & f){ }
        };
    

    bar.hpp (仅fwdecl foo的使用者,也消耗tar.hpp)

        #pragma once
    
        #include "tar.hpp" // tar consumed foo fwdecl-only
        #define FOO_FWD_ONLY
        #include "foo.hpp" // bar needs fwdecl-only
    
        #ifdef FOO_FWD_HPP
            #pragma message ( __FILE__ " has forward declaration of foo")
        #endif
        #ifdef FOO_HPP
            #pragma message ( __FILE__ " has full declaration of foo")
        #endif
    
        struct bar{
            bar(foo & f);
        };
    

    bar.cpp (bar和foo的完全decl使用者)

        #include "bar.hpp"
        #include "foo.hpp" // second inclusion, should enable full definition
    
        #ifdef FOO_FWD_HPP
            #pragma message ( __FILE__ " has forward declaration of foo")
        #endif
        #ifdef FOO_HPP
            #pragma message ( __FILE__ " has full declaration of foo")
        #endif
    
        bar::bar(foo& ref){
            ref.do_things();
        }
    

    baz.hpp (无依赖性)

        #pragma once
    
        struct baz{
            void do_baz();
        };
    

    baz.cpp (foo和baz的完全decl使用者)

        #include "baz.hpp"
        #include "foo.hpp"  // no prior include of foo, but since FOO_FWD_ONLY is not defined
                            // baz.cpp will get full declaration.
    
        #ifdef FOO_FWD_HPP
            #pragma message ( __FILE__ " has forward declaration of foo")
        #endif
        #ifdef FOO_HPP
            #pragma message ( __FILE__ " has full declaration of foo")
        #endif
    
    
        void baz::do_baz(){
            foo f;
            f.do_things(); // completely fine.
        }
    

    main.cpp (正在使用的应用程序)

        // consuming application
        #include "tar.hpp"
        #include "bar.hpp"
        #include "foo.hpp"  // already has previous foo fwdecl, so second inclusion will enable full declaration.
                            // (also FOO_FWD_ONLY is not defined, so first inclusion would enable it too)
        #include "baz.hpp"
        int main(void){
    
            foo f;
            tar t(f);
            bar 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
    

    最佳答案

    这是一个可供考虑的建议(也许不是最好的)。它涉及包含文件的包含程序,该包含程序设置宏以指示它仅需要该包含文件的前向声明。未定义宏时,它将从包含文件中获取所有内容。为避免头文件出现问题而忘记了以后取消定义特殊宏,可以使包含的文件负责取消定义该宏。

    它是这样的:

    foo.hpp

    #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
    

    bar.hpp
    #pragma once
    
    // only need forward declarations from foo.hpp
    #define FOO_FWD_ONLY
    #include "foo.hpp"
    
    struct bar {
        bar(const foo& f);
    };
    

    bar.cpp
    #include "bar.hpp"
    #include "foo.hpp"
    
    bar::bar(const foo& f){
        f.rab(); // requires actual implementation
    }
    

    主要好处是减少了编译的代码量。它无法解决意外暴露的问题。例如,如果“bar.hpp”包含某个其他文件,而该文件包含“foo.hpp”而未先定义FOO_FWD_ONLY宏,则“foo.hpp”中的完整定义将暴露给“bar.hpp”的其余部分。

    关于c++ - 是否可以将前向声明和正则声明合并为单个文件,然后像分隔开文件一样使用它?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/58630500/

    10-12 21:21