c++ - C++中的多重继承困境

标签 c++ multiple-inheritance

我在设计我的 C++ 库时遇到了问题。它是一个用于读取流的库,支持我在其他“流”实现中没有找到的功能。我决定开始写它的原因并不重要。关键是我有一个流类,它通过多重继承提供了两个重要的行为:可共享性和可搜索性。

可共享流是那些具有 shareBlock(size_t length) 方法的流,该方法返回一个与其父流共享资源的新流(例如,使用父流使用的相同内存块)。可搜索的流是那些……好吧,可搜索的流。通过 seek() 方法,这些类可以查找到流中的给定点。并非图书馆的所有流都是可共享和/或可搜索的。

同时提供查找和共享资源实现的流类继承称为Seekable 和Shareable 的接口(interface)类。如果我知道这种流的类型,那很好,但是,有时,我可能希望一个函数接受一个流作为参数,该流只满足同时可搜索和可共享的质量,而不管它实际上是哪个流类是。我可以创建另一个继承 Seekable 和 Shareable 的类并引用该类型,但是我必须让我的可搜索和可共享的类继承自该类。如果要添加更多像这样的“行为类”,我将需要在代码的各个地方进行多次修改,很快就会导致代码无法维护。有办法解决这个难题吗?如果不是,那么我绝对会明白为什么人们对多重继承不满意。它几乎完成了这项工作,但就在那时,它没有:D

感谢任何帮助。

-- 第二次编辑,首选问题解决方案 --

起初我以为Managu's解决方案将是我的首选。然而,Matthieu M.有另一个我更喜欢 Managu 的:使用 boost::enable_if<> .如果 BOOST_MPL_ASSERT,我想使用 Managu 的解决方案生成的消息并不那么令人毛骨悚然。如果有任何方法可以创建有指导意义的编译时错误消息,我肯定会这样做。但是,正如我所说,可用的方法会产生令人毛骨悚然的信息。因此,我更喜欢 boost::enable_if<> 时产生的(多)较少指导性但更清晰的信息。不满足条件。

我创建了一些宏来简化编写模板函数的任务,这些模板函数采用继承选定类类型的参数,它们在这里:

// SonettoEnableIfDerivedMacros.h
#ifndef SONETTO_ENABLEIFDERIVEDMACROS_H
#define SONETTO_ENABLEIFDERIVEDMACROS_H

#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/array/elem.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/mpl/and.hpp>
#include <boost/type_traits/is_base_and_derived.hpp>
#include <boost/utility/enable_if.hpp>

/*
    For each (TemplateArgument,DerivedClassType) preprocessor tuple,
    expand: `boost::is_base_and_derived<DerivedClassType,TemplateArgument>,'
*/
#define SONETTO_ENABLE_IF_DERIVED_EXPAND_CONDITION(z,n,data) \
        boost::is_base_and_derived<BOOST_PP_TUPLE_ELEM(2,1,BOOST_PP_ARRAY_ELEM(n,data)), \
                BOOST_PP_TUPLE_ELEM(2,0,BOOST_PP_ARRAY_ELEM(n,data))>,

/*
    ReturnType: Return type of the function
    DerivationsArray: Boost.Preprocessor array containing tuples in the form
            (TemplateArgument,DerivedClassType) (see
                    SONETTO_ENABLE_IF_DERIVED_EXPAND_CONDITION)

    Expands:
    typename boost::enable_if<
            boost::mpl::and_<
                    boost::is_base_and_derived<DerivedClassType,TemplateArgument>,
                    ...
                    boost::mpl::bool_<true> // Used to nullify trailing comma
            >, ReturnType>::type
*/
#define SONETTO_ENABLE_IF_DERIVED(ReturnType,DerivationsArray) \
        typename boost::enable_if< \
                boost::mpl::and_< \
                        BOOST_PP_REPEAT(BOOST_PP_ARRAY_SIZE(DerivationsArray), \
                            SONETTO_ENABLE_IF_DERIVED_EXPAND_CONDITION,DerivationsArray) \
                        boost::mpl::bool_<true> \
            >, ReturnType>::type

#endif

// main.cpp: Usage example
#include <iostream>
#include "SonettoEnableIfDerivedMacros.h"

class BehaviourA
{
public:
    void behaveLikeA() const { std::cout << "behaveLikeA()\n"; }
};

class BehaviourB
{
public:
    void behaveLikeB() const { std::cout << "behaveLikeB()\n"; }
};

class BehaviourC
{
public:
    void behaveLikeC() const { std::cout << "behaveLikeC()\n"; }
};

class CompoundBehaviourAB : public BehaviourA, public BehaviourB {};
class CompoundBehaviourAC : public BehaviourA, public BehaviourC {};
class SingleBehaviourA : public BehaviourA {};

template <class MustBeAB>
SONETTO_ENABLE_IF_DERIVED(void,(2,((MustBeAB,BehaviourA),(MustBeAB,BehaviourB))))
myFunction(MustBeAB &ab)
{
    ab.behaveLikeA();
    ab.behaveLikeB();
}

int main()
{
    CompoundBehaviourAB ab;
    CompoundBehaviourAC ac;
    SingleBehaviourA    a;

    myFunction(ab); // Ok, prints `behaveLikeA()' and `behaveLikeB()'
    myFunction(ac); // Fails with `error: no matching function for
                    // call to `myFunction(CompoundBehaviourAC&)''
    myFunction(a);  // Fails with `error: no matching function for
                    // call to `myFunction(SingleBehaviourA&)''
}

如您所见,错误消息非常干净(至少在 GCC 3.4.5 中是这样)。但它们可能会产生误导。它不会通知您传递了错误的参数类型。它通知您该函数不存在(事实上,这不是因为 SFINAE;但用户可能不太清楚)。不过,我更喜欢那些干净的信息而不是那些 randomStuff ... ************** garbage ************** BOOST_MPL_ASSERT产生。

如果您在此代码中发现任何错误,请编辑并更正它们,或就此发表评论。我在这些宏中发现的一个主要问题是它们受限于某些 Boost.Preprocessor 限制。比如这里我只能传一个DerivationsArray最多 4 项到 SONETTO_ENABLE_IF_DERIVED() .我认为这些限制是可以配置的,也许它们甚至会在即将到来的 C++1x 标准中被取消,不是吗?如果我错了,请纠正我。我不记得他们是否建议对预处理器进行更改。

谢谢。

最佳答案

一些想法:

STL 在迭代器和仿函数方面也存在同样的问题。那里的解决方案基本上是从等式中一起删除类型,记录需求(作为“概念”),并使用相当于鸭子类型的东西。这非常符合编译时多态性的策略。

也许折衷方案是创建一个模板函数,该函数在实例化时静态检查其条件。这是一个草图(我不保证会编译)。

class shareable {...};
class seekable {...};

template <typename StreamType>
void needs_sharable_and_seekable(const StreamType& stream)
{
    BOOST_STATIC_ASSERT(boost::is_base_and_derived<shareable, StreamType>::value);
    BOOST_STATIC_ASSERT(boost::is_base_and_derived<seekable, StreamType>::value);
    ....
}

编辑:花几分钟确保编译,并“清理”错误消息:

#include <boost/type_traits/is_base_and_derived.hpp>
#include <boost/mpl/assert.hpp>

class shareable {};
class seekable {};

class both : public shareable, public seekable
{
};


template <typename StreamType>
void dosomething(const StreamType& dummy)
{
  BOOST_MPL_ASSERT_MSG((boost::is_base_and_derived<shareable, StreamType>::value),
                       dosomething_requires_shareable_stream, 
                       (StreamType));
  BOOST_MPL_ASSERT_MSG((boost::is_base_and_derived<seekable, StreamType>::value),
                       dosomething_requires_seekable_stream, 
                       (StreamType));
}

int main()
{
  both b;
  shareable s1;
  seekable s2;
  dosomething(b);
  dosomething(s1);
  dosomething(s2);
}

关于c++ - C++中的多重继承困境,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1516398/

相关文章:

c++ - 使用未定义类型

c++ - 使用MinGW单独编译

c++ - 在多个 DLL 中分离类实现

c++ - C++编译器在多重继承的情况下如何处理成员变量内存偏移?

c++ - MSVC 处理大量包含路径

c++ - 查看编译器生成的默认函数?

C++ 多重继承和虚表

c++ - 地址、reinterpret_cast 和多重继承

c++ - 使用多重继承时如何避免死亡钻石?

c++ - 如何覆盖多重继承中具有相同名称的基类的虚函数?