代码之家  ›  专栏  ›  技术社区  ›  Aykhan Hagverdili

要在构造函数之后运行的初始化块

  •  2
  • Aykhan Hagverdili  · 技术社区  · 2 年前

    假设我有以下课程:

    template <class Base>
    struct Wrapper : public Base {
        using Base::Base;
        
        // ... add functionality ...
    };
    

    我希望在构造函数之后的构建过程中执行一些代码。我不能添加默认构造函数,因为当使用继承的构造函数时,它不会运行。一个想法是:

    template <class Base>
    struct Wrapper : public Base {
        bool _ = [this] {
            // initialize ... 
            return true;
        }();
    
        using Base::Base;
    };
    

    只要你把它作为最后一个成员,这就可以很好地工作,但这会浪费内存。

    另一种方式是:

    #include <type_traits>
    
    template <class T>
    struct InitBlock {
        InitBlock() {
            static_cast<T*>(this)->init();
        }
    };
    
    template <class Base>
    struct Wrapper : public Base, private InitBlock<Wrapper<Base>> {
    private:
        template <class T>
        friend struct InitBlock;
    
        void init() {
            // initialize ...
        }
    
    public:
        using Base::Base;
    };
    

    这很好,但有点冗长。没有任何保护 init 避免在其他地方再次被叫。此外,如果Wrapper添加成员,则称为 之前 这些都已初始化,所以并不理想。

    什么是更好的(安全且低成本的)方法?

    1 回复  |  直到 2 年前
        1
  •  1
  •   Aykhan Hagverdili    2 年前

    As mentioned ,我们可以使用 [[no_unique_address]] 以避免为空类分配内存。它受到了除MSVC之外的所有主要编译器的尊敬。MSVC有自己的扩展 [[msvc::no_unique_address]] 。我们将一个宏包裹在这个周围,它运行良好:

    #include <stdio.h>
    
    #ifdef _MSC_VER
    #define NO_UNIQUE_ADDRESS [[msvc::no_unique_address]]
    #else 
    #define NO_UNIQUE_ADDRESS [[no_unique_address]]
    #endif
    
    template <class Base>
    struct Wrapper : public Base {
    private:
        NO_UNIQUE_ADDRESS struct Init {} _ = [] {
            puts("Wrapper init");
            return Init{};
        }();
    public:
        using Base::Base;
    };
    
    
    struct Foo {
        int i;
    
        Foo(int i) : i(i) {
            printf("Foo(%d)\n", i);
        }
    };
    
    int main() {
        Wrapper<Foo> wfoo(42);
        Foo foo(43);
        static_assert(sizeof(wfoo) == sizeof(foo));
    }
    

    你仍然需要注意把它放在所有其他成员之后,以确保在我们触摸他们时他们已经初始化,所以这并不理想。

    See online