在类接口中直接访问成员的成员而无需继承

时间:2018-08-19 00:59:22

标签: c++ inheritance constructor c++14 member

我面临的问题是,在我的应用程序中,重要的是在构造函数中初始化类成员的顺序。因此,我需要使用笨拙的语法来获得我想要的行为。

这应该概述我的问题(请参见下面的代码):从用户的角度来看,我希望我的class Widget包含class WidgetSignals的界面。我不能使用继承,因为这要求我在应该用于初始化WidgetSignals的{​​{1}}成员elements的成员Widget之前初始化继承的WidgetSignals

template<typename... Elems>
class WidgetSignals
{
    WidgetSignals(const std::tuple<Elems...> elems)
        : // initialize members using elems
    {}
    // members ...
};

template<typename... Elems>
class Widget : public WidgetSignals<Elems...>
{
    std::tuple<Elems...> elements;

    Widget(vec4 quad)
        : WidgetSignals<Elems...>(elements) // uh-oh! elements are not initialized yet!
        , elements(initalizeElements(quad))
};

这是我以前针对此问题的解决方案:

使用中介者帮助程序类:

template<typename... Elems>
class Widget : public WidgetSignals<Elems...>
{
    std::tuple<Elems...> elements;

    struct Data
    {
        Data(vec4 quad)
            : elems(initializeElements(quad))  // initialize elements here
        {}        
        std::tuple<Elems...> elems;
    };
    Widget(Data data)
        : WidgetSignals<Elems...>(data.elems) // bingo!
        , elements(data.elems)
    {}
};

封装WidgetSignals并在小部件中公开对WidgetSignals'成员的引用:

template<typename... Elems>
class Widget
{
    std::tuple<Elems...> elements;
    WidgetSignals<Elems...> signals;

    Widget(vec4 quad)
        : elements(initializeElements(quad))
        , signals(elements) // initialized after elements because of order of member declaration
    {}
    // WidgetSignal member references
    const typename WidgetSignals<Elems...>::SignalType& enter = signals.enter;
    const typename WidgetSignals<Elems...>::SignalType& leave = signals.leave;
    // ... remaining members
};

使用这两种解决方案,我都可以通过WidgetSignals使用Widget的接口:

Widget widget(vec4(0, 0, 20, 5));
foo(widget.enter);

但是这两种解决方案都相当笨拙和混乱,因此我真的想为此提供更好的语法,例如:

using signals;

using signals::enter;
Widget

using WidgetSignals<Elems...>::enter;实际上是可行的,但前提是Widget已从WidgetSignals继承而来,这意味着我不得不再次使用中介者帮助器类,这是我想避免的。 / p>

1 个答案:

答案 0 :(得分:3)

只需将要初始化的元素放在首先继承的[private]基类中即可。例如:

template<typename... Elems>
struct WidgetPbase {
    std::tuple<Elems...> elements;
}
template<typename... Elems>
class Widget
    : private WidgetPbase<Elems...>
    , public WidgetSignals<Elems...>
{
public:
    Widget(vec4 quad)
        : WidgetPbase<Elems...>{initializeElements(quad)}
        , WidgetSignals<Elems...>(elements) {
    }
};

作为从左到右/从上到下继承的基类,这安排elements成员在被访问时进行初始化。唯一可能的警告是首先初始化virtual个碱基:如果WdigetSignalsvirtual碱基,则可能有必要在virtual前面加上一个WidgetPbase